HDFS-12860. StripedBlockUtil#getRangesInternalBlocks throws exception for the block group size larger than 2GB. (Contributed by Lei (Eddy) Xu)

This commit is contained in:
Lei Xu 2018-01-04 10:16:40 -08:00
parent 739d3c394d
commit dc735b286b
2 changed files with 86 additions and 18 deletions

View File

@ -396,7 +396,9 @@ public class StripedBlockUtil {
long rangeStartInBlockGroup, long rangeEndInBlockGroup) { long rangeStartInBlockGroup, long rangeEndInBlockGroup) {
Preconditions.checkArgument( Preconditions.checkArgument(
rangeStartInBlockGroup <= rangeEndInBlockGroup && rangeStartInBlockGroup <= rangeEndInBlockGroup &&
rangeEndInBlockGroup < blockGroup.getBlockSize()); rangeEndInBlockGroup < blockGroup.getBlockSize(),
"start=%s end=%s blockSize=%s", rangeStartInBlockGroup,
rangeEndInBlockGroup, blockGroup.getBlockSize());
long len = rangeEndInBlockGroup - rangeStartInBlockGroup + 1; long len = rangeEndInBlockGroup - rangeStartInBlockGroup + 1;
int firstCellIdxInBG = (int) (rangeStartInBlockGroup / cellSize); int firstCellIdxInBG = (int) (rangeStartInBlockGroup / cellSize);
int lastCellIdxInBG = (int) (rangeEndInBlockGroup / cellSize); int lastCellIdxInBG = (int) (rangeEndInBlockGroup / cellSize);
@ -578,28 +580,39 @@ public class StripedBlockUtil {
public static class StripingCell { public static class StripingCell {
final ErasureCodingPolicy ecPolicy; final ErasureCodingPolicy ecPolicy;
/** Logical order in a block group, used when doing I/O to a block group. */ /** Logical order in a block group, used when doing I/O to a block group. */
final int idxInBlkGroup; private final long idxInBlkGroup;
final int idxInInternalBlk; private final long idxInInternalBlk;
final int idxInStripe; private final int idxInStripe;
/** /**
* When a logical byte range is mapped to a set of cells, it might * When a logical byte range is mapped to a set of cells, it might
* partially overlap with the first and last cells. This field and the * partially overlap with the first and last cells. This field and the
* {@link #size} variable represent the start offset and size of the * {@link #size} variable represent the start offset and size of the
* overlap. * overlap.
*/ */
final int offset; private final long offset;
final int size; private final int size;
StripingCell(ErasureCodingPolicy ecPolicy, int cellSize, int idxInBlkGroup, StripingCell(ErasureCodingPolicy ecPolicy, int cellSize, long idxInBlkGroup,
int offset) { long offset) {
this.ecPolicy = ecPolicy; this.ecPolicy = ecPolicy;
this.idxInBlkGroup = idxInBlkGroup; this.idxInBlkGroup = idxInBlkGroup;
this.idxInInternalBlk = idxInBlkGroup / ecPolicy.getNumDataUnits(); this.idxInInternalBlk = idxInBlkGroup / ecPolicy.getNumDataUnits();
this.idxInStripe = idxInBlkGroup - this.idxInStripe = (int)(idxInBlkGroup -
this.idxInInternalBlk * ecPolicy.getNumDataUnits(); this.idxInInternalBlk * ecPolicy.getNumDataUnits());
this.offset = offset; this.offset = offset;
this.size = cellSize; this.size = cellSize;
} }
int getIdxInStripe() {
return idxInStripe;
}
@Override
public String toString() {
return String.format("StripingCell(idxInBlkGroup=%d, " +
"idxInInternalBlk=%d, idxInStrip=%d, offset=%d, size=%d)",
idxInBlkGroup, idxInInternalBlk, idxInStripe, offset, size);
}
} }
/** /**
@ -646,7 +659,9 @@ public class StripedBlockUtil {
public int missingChunksNum = 0; public int missingChunksNum = 0;
public AlignedStripe(long offsetInBlock, long length, int width) { public AlignedStripe(long offsetInBlock, long length, int width) {
Preconditions.checkArgument(offsetInBlock >= 0 && length >= 0); Preconditions.checkArgument(offsetInBlock >= 0 && length >= 0,
"OffsetInBlock(%s) and length(%s) must be non-negative",
offsetInBlock, length);
this.range = new VerticalRange(offsetInBlock, length); this.range = new VerticalRange(offsetInBlock, length);
this.chunks = new StripingChunk[width]; this.chunks = new StripingChunk[width];
} }
@ -665,9 +680,9 @@ public class StripedBlockUtil {
@Override @Override
public String toString() { public String toString() {
return "Offset=" + range.offsetInBlock + ", length=" + range.spanInBlock + return "AlignedStripe(Offset=" + range.offsetInBlock + ", length=" +
", fetchedChunksNum=" + fetchedChunksNum + range.spanInBlock + ", fetchedChunksNum=" + fetchedChunksNum +
", missingChunksNum=" + missingChunksNum; ", missingChunksNum=" + missingChunksNum + ")";
} }
} }
@ -698,7 +713,9 @@ public class StripedBlockUtil {
public long spanInBlock; public long spanInBlock;
public VerticalRange(long offsetInBlock, long length) { public VerticalRange(long offsetInBlock, long length) {
Preconditions.checkArgument(offsetInBlock >= 0 && length >= 0); Preconditions.checkArgument(offsetInBlock >= 0 && length >= 0,
"OffsetInBlock(%s) and length(%s) must be non-negative",
offsetInBlock, length);
this.offsetInBlock = offsetInBlock; this.offsetInBlock = offsetInBlock;
this.spanInBlock = length; this.spanInBlock = length;
} }
@ -707,6 +724,12 @@ public class StripedBlockUtil {
public boolean include(long pos) { public boolean include(long pos) {
return pos >= offsetInBlock && pos < offsetInBlock + spanInBlock; return pos >= offsetInBlock && pos < offsetInBlock + spanInBlock;
} }
@Override
public String toString() {
return String.format("VerticalRange(offsetInBlock=%d, spanInBlock=%d)",
this.offsetInBlock, this.spanInBlock);
}
} }
/** /**
@ -880,7 +903,9 @@ public class StripedBlockUtil {
final long length; final long length;
public StripeRange(long offsetInBlock, long length) { public StripeRange(long offsetInBlock, long length) {
Preconditions.checkArgument(offsetInBlock >= 0 && length >= 0); Preconditions.checkArgument(offsetInBlock >= 0 && length >= 0,
"Offset(%s) and length(%s) must be non-negative", offsetInBlock,
length);
this.offsetInBlock = offsetInBlock; this.offsetInBlock = offsetInBlock;
this.length = length; this.length = length;
} }
@ -892,6 +917,12 @@ public class StripedBlockUtil {
public long getLength() { public long getLength() {
return length; return length;
} }
@Override
public String toString() {
return String.format("StripeRange(offsetInBlock=%d, length=%d)",
offsetInBlock, length);
}
} }
/** /**

View File

@ -40,6 +40,7 @@ import java.util.Random;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/** /**
* Need to cover the following combinations: * Need to cover the following combinations:
@ -127,7 +128,7 @@ public class TestStripedBlockUtil {
return (byte) (((i + 13) * 29) & BYTE_MASK); return (byte) (((i + 13) * 29) & BYTE_MASK);
} }
private LocatedStripedBlock createDummyLocatedBlock(int bgSize) { private LocatedStripedBlock createDummyLocatedBlock(long bgSize) {
final long blockGroupID = -1048576; final long blockGroupID = -1048576;
DatanodeInfo[] locs = new DatanodeInfo[groupSize]; DatanodeInfo[] locs = new DatanodeInfo[groupSize];
String[] storageIDs = new String[groupSize]; String[] storageIDs = new String[groupSize];
@ -160,7 +161,7 @@ public class TestStripedBlockUtil {
Preconditions.checkState(done % cellSize == 0); Preconditions.checkState(done % cellSize == 0);
StripingCell cell = StripingCell cell =
new StripingCell(ecPolicy, cellSize, done / cellSize, 0); new StripingCell(ecPolicy, cellSize, done / cellSize, 0);
int idxInStripe = cell.idxInStripe; int idxInStripe = cell.getIdxInStripe();
int size = Math.min(cellSize, bgSize - done); int size = Math.min(cellSize, bgSize - done);
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
bufs[idxInStripe][pos[idxInStripe] + i] = hashIntToByte(done + i); bufs[idxInStripe][pos[idxInStripe] + i] = hashIntToByte(done + i);
@ -283,4 +284,40 @@ public class TestStripedBlockUtil {
} }
} }
} }
/**
* Test dividing a byte range that located above the 2GB range, which is
* {@link Integer#MAX_VALUE}.
*
* HDFS-12860 occurs when {@link VerticalRange#offsetInBlock} is larger than
* {@link Integer#MAX_VALUE}
*
* Take RS-6-3-1024k EC policy as example:
* <li>cellSize = 1MB</li>
* <li>The first {@link VerticalRange#offsetInBlock} that is larger than
* {@link Integer#MAX_VALUE} is Math.ceilInteger.MAX_VALUE / cellSize = 2048
* </li>
* <li>The first offset in block group that causes HDFS-12860 is:
* 2048 * cellSize * dataBlocks (6)</li>
*/
@Test
public void testDivideOneStripeLargeBlockSize() {
ByteBuffer buffer = ByteBuffer.allocate(stripeSize);
// This offset will cause overflow before HDFS-12860.
long offsetInInternalBlk = Integer.MAX_VALUE / cellSize + 10;
long rangeStartInBlockGroup = offsetInInternalBlk * dataBlocks * cellSize;
long rangeEndInBlockGroup = rangeStartInBlockGroup +
dataBlocks / 2 * cellSize - 1;
// each block is 4GB, each block group has 4GB * (6 + 3) = 36GB.
long blockGroupSize = 4096L * cellSize * groupSize;
LocatedStripedBlock blockGroup = createDummyLocatedBlock(blockGroupSize);
AlignedStripe[] stripes = StripedBlockUtil.divideOneStripe(ecPolicy,
cellSize, blockGroup, rangeStartInBlockGroup, rangeEndInBlockGroup,
buffer);
long offset = offsetInInternalBlk * cellSize;
assertTrue(offset > Integer.MAX_VALUE);
assertEquals(offset, stripes[0].range.offsetInBlock);
assertEquals(1, stripes.length);
}
} }