HDFS-4199. Provide test for HdfsVolumeId. Contributed by Ivan A. Veselovsky.
git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/branches/branch-2@1417264 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
8be0df5b4b
commit
d6a9d9f76c
|
@ -126,6 +126,8 @@ Release 2.0.3-alpha - Unreleased
|
||||||
HDFS-4214. OfflineEditsViewer should print out the offset at which it
|
HDFS-4214. OfflineEditsViewer should print out the offset at which it
|
||||||
encountered an error. (Colin Patrick McCabe via atm)
|
encountered an error. (Colin Patrick McCabe via atm)
|
||||||
|
|
||||||
|
HDFS-4199. Provide test for HdfsVolumeId. (Ivan A. Veselovsky via atm)
|
||||||
|
|
||||||
OPTIMIZATIONS
|
OPTIMIZATIONS
|
||||||
|
|
||||||
BUG FIXES
|
BUG FIXES
|
||||||
|
|
|
@ -27,26 +27,38 @@ import org.apache.hadoop.classification.InterfaceStability;
|
||||||
* HDFS-specific volume identifier which implements {@link VolumeId}. Can be
|
* HDFS-specific volume identifier which implements {@link VolumeId}. Can be
|
||||||
* used to differentiate between the data directories on a single datanode. This
|
* used to differentiate between the data directories on a single datanode. This
|
||||||
* identifier is only unique on a per-datanode basis.
|
* identifier is only unique on a per-datanode basis.
|
||||||
|
*
|
||||||
|
* Note that invalid IDs are represented by {@link VolumeId#INVALID_VOLUME_ID}.
|
||||||
*/
|
*/
|
||||||
@InterfaceStability.Unstable
|
@InterfaceStability.Unstable
|
||||||
@InterfaceAudience.Public
|
@InterfaceAudience.Public
|
||||||
public class HdfsVolumeId implements VolumeId {
|
public class HdfsVolumeId implements VolumeId {
|
||||||
|
|
||||||
private final byte[] id;
|
private final byte[] id;
|
||||||
private final boolean isValid;
|
|
||||||
|
|
||||||
public HdfsVolumeId(byte[] id, boolean isValid) {
|
public HdfsVolumeId(byte[] id) {
|
||||||
|
if (id == null) {
|
||||||
|
throw new NullPointerException("A valid Id can only be constructed " +
|
||||||
|
"with a non-null byte array.");
|
||||||
|
}
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.isValid = isValid;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isValid() {
|
public final boolean isValid() {
|
||||||
return isValid;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int compareTo(VolumeId arg0) {
|
public int compareTo(VolumeId arg0) {
|
||||||
|
if (arg0 == null) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (!arg0.isValid()) {
|
||||||
|
// any valid ID is greater
|
||||||
|
// than any invalid ID:
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
return hashCode() - arg0.hashCode();
|
return hashCode() - arg0.hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,8 +75,10 @@ public class HdfsVolumeId implements VolumeId {
|
||||||
if (obj == this) {
|
if (obj == this) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
HdfsVolumeId that = (HdfsVolumeId) obj;
|
HdfsVolumeId that = (HdfsVolumeId) obj;
|
||||||
|
// NB: if (!obj.isValid()) { return false; } check is not necessary
|
||||||
|
// because we have class identity checking above, and for this class
|
||||||
|
// isValid() is always true.
|
||||||
return new EqualsBuilder().append(this.id, that.id).isEquals();
|
return new EqualsBuilder().append(this.id, that.id).isEquals();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,48 @@ import org.apache.hadoop.classification.InterfaceStability;
|
||||||
@InterfaceAudience.Public
|
@InterfaceAudience.Public
|
||||||
public interface VolumeId extends Comparable<VolumeId> {
|
public interface VolumeId extends Comparable<VolumeId> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an invalid Volume ID (ID for unknown content).
|
||||||
|
*/
|
||||||
|
public static final VolumeId INVALID_VOLUME_ID = new VolumeId() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(VolumeId arg0) {
|
||||||
|
// This object is equal only to itself;
|
||||||
|
// It is greater than null, and
|
||||||
|
// is always less than any other VolumeId:
|
||||||
|
if (arg0 == null) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (arg0 == this) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
// this object is equal only to itself:
|
||||||
|
return (obj == this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Integer.MIN_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isValid() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Invalid VolumeId";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates if the disk identifier is valid. Invalid identifiers indicate
|
* Indicates if the disk identifier is valid. Invalid identifiers indicate
|
||||||
* that the block was not present, or the location could otherwise not be
|
* that the block was not present, or the location could otherwise not be
|
||||||
|
|
|
@ -202,7 +202,7 @@ class BlockStorageLocationUtil {
|
||||||
ArrayList<VolumeId> l = new ArrayList<VolumeId>(b.getLocations().length);
|
ArrayList<VolumeId> l = new ArrayList<VolumeId>(b.getLocations().length);
|
||||||
// Start off all IDs as invalid, fill it in later with results from RPCs
|
// Start off all IDs as invalid, fill it in later with results from RPCs
|
||||||
for (int i = 0; i < b.getLocations().length; i++) {
|
for (int i = 0; i < b.getLocations().length; i++) {
|
||||||
l.add(new HdfsVolumeId(null, false));
|
l.add(VolumeId.INVALID_VOLUME_ID);
|
||||||
}
|
}
|
||||||
blockVolumeIds.put(b, l);
|
blockVolumeIds.put(b, l);
|
||||||
}
|
}
|
||||||
|
@ -236,7 +236,7 @@ class BlockStorageLocationUtil {
|
||||||
// Get the VolumeId by indexing into the list of VolumeIds
|
// Get the VolumeId by indexing into the list of VolumeIds
|
||||||
// provided by the datanode
|
// provided by the datanode
|
||||||
byte[] volumeId = metaVolumeIds.get(volumeIndex);
|
byte[] volumeId = metaVolumeIds.get(volumeIndex);
|
||||||
HdfsVolumeId id = new HdfsVolumeId(volumeId, true);
|
HdfsVolumeId id = new HdfsVolumeId(volumeId);
|
||||||
// Find out which index we are in the LocatedBlock's replicas
|
// Find out which index we are in the LocatedBlock's replicas
|
||||||
LocatedBlock locBlock = extBlockToLocBlock.get(extBlock);
|
LocatedBlock locBlock = extBlockToLocBlock.get(extBlock);
|
||||||
DatanodeInfo[] dnInfos = locBlock.getLocations();
|
DatanodeInfo[] dnInfos = locBlock.getLocations();
|
||||||
|
|
|
@ -0,0 +1,183 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.fs;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
public class TestVolumeId {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEquality() {
|
||||||
|
final VolumeId id1 = new HdfsVolumeId(new byte[] { (byte)0, (byte)0 });
|
||||||
|
testEq(true, id1, id1);
|
||||||
|
|
||||||
|
final VolumeId id2 = new HdfsVolumeId(new byte[] { (byte)0, (byte)1 });
|
||||||
|
testEq(true, id2, id2);
|
||||||
|
testEq(false, id1, id2);
|
||||||
|
|
||||||
|
final VolumeId id3 = new HdfsVolumeId(new byte[] { (byte)1, (byte)0 });
|
||||||
|
testEq(true, id3, id3);
|
||||||
|
testEq(false, id1, id3);
|
||||||
|
|
||||||
|
// same as 2, but "invalid":
|
||||||
|
final VolumeId id2copy1 = new HdfsVolumeId(new byte[] { (byte)0, (byte)1 });
|
||||||
|
|
||||||
|
testEq(true, id2, id2copy1);
|
||||||
|
|
||||||
|
// same as 2copy1:
|
||||||
|
final VolumeId id2copy2 = new HdfsVolumeId(new byte[] { (byte)0, (byte)1 });
|
||||||
|
|
||||||
|
testEq(true, id2, id2copy2);
|
||||||
|
|
||||||
|
testEqMany(true, new VolumeId[] { id2, id2copy1, id2copy2 });
|
||||||
|
|
||||||
|
testEqMany(false, new VolumeId[] { id1, id2, id3 });
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private <T> void testEq(final boolean eq, Comparable<? super T> id1, Comparable<? super T> id2) {
|
||||||
|
final int h1 = id1.hashCode();
|
||||||
|
final int h2 = id2.hashCode();
|
||||||
|
|
||||||
|
// eq reflectivity:
|
||||||
|
assertTrue(id1.equals(id1));
|
||||||
|
assertTrue(id2.equals(id2));
|
||||||
|
assertEquals(0, id1.compareTo((T)id1));
|
||||||
|
assertEquals(0, id2.compareTo((T)id2));
|
||||||
|
|
||||||
|
// eq symmetry:
|
||||||
|
assertEquals(eq, id1.equals(id2));
|
||||||
|
assertEquals(eq, id2.equals(id1));
|
||||||
|
|
||||||
|
// null comparison:
|
||||||
|
assertFalse(id1.equals(null));
|
||||||
|
assertFalse(id2.equals(null));
|
||||||
|
|
||||||
|
// compareTo:
|
||||||
|
assertEquals(eq, 0 == id1.compareTo((T)id2));
|
||||||
|
assertEquals(eq, 0 == id2.compareTo((T)id1));
|
||||||
|
// compareTo must be antisymmetric:
|
||||||
|
assertEquals(sign(id1.compareTo((T)id2)), -sign(id2.compareTo((T)id1)));
|
||||||
|
|
||||||
|
// compare with null should never return 0 to be consistent with #equals():
|
||||||
|
assertTrue(id1.compareTo(null) != 0);
|
||||||
|
assertTrue(id2.compareTo(null) != 0);
|
||||||
|
|
||||||
|
// check that hash codes did not change:
|
||||||
|
assertEquals(h1, id1.hashCode());
|
||||||
|
assertEquals(h2, id2.hashCode());
|
||||||
|
if (eq) {
|
||||||
|
// in this case the hash codes must be the same:
|
||||||
|
assertEquals(h1, h2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int sign(int x) {
|
||||||
|
if (x == 0) {
|
||||||
|
return 0;
|
||||||
|
} else if (x > 0) {
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private <T> void testEqMany(final boolean eq, Comparable<? super T>... volumeIds) {
|
||||||
|
Comparable<? super T> vidNext;
|
||||||
|
int sum = 0;
|
||||||
|
for (int i=0; i<volumeIds.length; i++) {
|
||||||
|
if (i == volumeIds.length - 1) {
|
||||||
|
vidNext = volumeIds[0];
|
||||||
|
} else {
|
||||||
|
vidNext = volumeIds[i + 1];
|
||||||
|
}
|
||||||
|
testEq(eq, volumeIds[i], vidNext);
|
||||||
|
sum += sign(volumeIds[i].compareTo((T)vidNext));
|
||||||
|
}
|
||||||
|
// the comparison relationship must always be acyclic:
|
||||||
|
assertTrue(sum < volumeIds.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Test HdfsVolumeId(new byte[0]) instances: show that we permit such
|
||||||
|
* objects, they are still valid, and obey the same equality
|
||||||
|
* rules other objects do.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testIdEmptyBytes() {
|
||||||
|
final VolumeId idEmpty1 = new HdfsVolumeId(new byte[0]);
|
||||||
|
assertTrue(idEmpty1.isValid());
|
||||||
|
final VolumeId idEmpty2 = new HdfsVolumeId(new byte[0]);
|
||||||
|
assertTrue(idEmpty2.isValid());
|
||||||
|
final VolumeId idNotEmpty = new HdfsVolumeId(new byte[] { (byte)1 });
|
||||||
|
assertTrue(idNotEmpty.isValid());
|
||||||
|
|
||||||
|
testEq(true, idEmpty1, idEmpty2);
|
||||||
|
testEq(false, idEmpty1, idNotEmpty);
|
||||||
|
testEq(false, idEmpty2, idNotEmpty);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Test the VolumeId.INVALID_VOLUME_ID singleton.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testInvalidId() {
|
||||||
|
try {
|
||||||
|
new HdfsVolumeId(null);
|
||||||
|
assertTrue("NPE expected.", false);
|
||||||
|
} catch (NullPointerException npe) {
|
||||||
|
// okay
|
||||||
|
}
|
||||||
|
final VolumeId idEmpty = new HdfsVolumeId(new byte[] {});
|
||||||
|
final VolumeId idNotEmpty = new HdfsVolumeId(new byte[] { (byte)1 });
|
||||||
|
|
||||||
|
testEq(false, VolumeId.INVALID_VOLUME_ID, idNotEmpty);
|
||||||
|
testEq(false, VolumeId.INVALID_VOLUME_ID, idEmpty);
|
||||||
|
|
||||||
|
testEqMany(true,
|
||||||
|
new VolumeId[] {
|
||||||
|
VolumeId.INVALID_VOLUME_ID,
|
||||||
|
VolumeId.INVALID_VOLUME_ID,
|
||||||
|
VolumeId.INVALID_VOLUME_ID } );
|
||||||
|
testEqMany(false,
|
||||||
|
new VolumeId[] {
|
||||||
|
VolumeId.INVALID_VOLUME_ID,
|
||||||
|
idEmpty,
|
||||||
|
idNotEmpty });
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* test #toString() for typical VolumeId equality classes
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testToString() {
|
||||||
|
// The #toString() return value is only checked for != null.
|
||||||
|
// We cannot assert more.
|
||||||
|
String strInvalid = VolumeId.INVALID_VOLUME_ID.toString();
|
||||||
|
assertNotNull(strInvalid);
|
||||||
|
|
||||||
|
String strEmpty = new HdfsVolumeId(new byte[] {}).toString();
|
||||||
|
assertNotNull(strEmpty);
|
||||||
|
|
||||||
|
String strNotEmpty = new HdfsVolumeId(new byte[] { (byte)1 }).toString();
|
||||||
|
assertNotNull(strNotEmpty);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue