|
@@ -19,6 +19,9 @@ package org.apache.hadoop.util;
|
|
|
|
|
|
import java.nio.ByteBuffer;
|
|
import java.nio.ByteBuffer;
|
|
import java.util.Random;
|
|
import java.util.Random;
|
|
|
|
+import java.util.concurrent.TimeUnit;
|
|
|
|
+
|
|
|
|
+import com.google.common.base.Stopwatch;
|
|
|
|
|
|
import org.apache.hadoop.fs.ChecksumException;
|
|
import org.apache.hadoop.fs.ChecksumException;
|
|
import org.junit.Test;
|
|
import org.junit.Test;
|
|
@@ -53,68 +56,113 @@ public class TestDataChecksum {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-
|
|
|
|
- private void doBulkTest(DataChecksum checksum, int dataLength,
|
|
|
|
- boolean useDirect) throws Exception {
|
|
|
|
- System.err.println("Testing bulk checksums of length " +
|
|
|
|
- dataLength + " with " +
|
|
|
|
- (useDirect ? "direct" : "array-backed") + " buffers");
|
|
|
|
- int numSums = (dataLength - 1)/checksum.getBytesPerChecksum() + 1;
|
|
|
|
- int sumsLength = numSums * checksum.getChecksumSize();
|
|
|
|
-
|
|
|
|
- byte data[] = new byte[dataLength +
|
|
|
|
- DATA_OFFSET_IN_BUFFER +
|
|
|
|
- DATA_TRAILER_IN_BUFFER];
|
|
|
|
- new Random().nextBytes(data);
|
|
|
|
- ByteBuffer dataBuf = ByteBuffer.wrap(
|
|
|
|
|
|
+
|
|
|
|
+ private static class Harness {
|
|
|
|
+ final DataChecksum checksum;
|
|
|
|
+ final int dataLength, sumsLength, numSums;
|
|
|
|
+ ByteBuffer dataBuf, checksumBuf;
|
|
|
|
+
|
|
|
|
+ Harness(DataChecksum checksum, int dataLength, boolean useDirect) {
|
|
|
|
+ this.checksum = checksum;
|
|
|
|
+ this.dataLength = dataLength;
|
|
|
|
+
|
|
|
|
+ numSums = (dataLength - 1)/checksum.getBytesPerChecksum() + 1;
|
|
|
|
+ sumsLength = numSums * checksum.getChecksumSize();
|
|
|
|
+
|
|
|
|
+ byte data[] = new byte[dataLength +
|
|
|
|
+ DATA_OFFSET_IN_BUFFER +
|
|
|
|
+ DATA_TRAILER_IN_BUFFER];
|
|
|
|
+ new Random().nextBytes(data);
|
|
|
|
+ dataBuf = ByteBuffer.wrap(
|
|
data, DATA_OFFSET_IN_BUFFER, dataLength);
|
|
data, DATA_OFFSET_IN_BUFFER, dataLength);
|
|
|
|
|
|
- byte checksums[] = new byte[SUMS_OFFSET_IN_BUFFER + sumsLength];
|
|
|
|
- ByteBuffer checksumBuf = ByteBuffer.wrap(
|
|
|
|
|
|
+ byte checksums[] = new byte[SUMS_OFFSET_IN_BUFFER + sumsLength];
|
|
|
|
+ checksumBuf = ByteBuffer.wrap(
|
|
checksums, SUMS_OFFSET_IN_BUFFER, sumsLength);
|
|
checksums, SUMS_OFFSET_IN_BUFFER, sumsLength);
|
|
-
|
|
|
|
- // Swap out for direct buffers if requested.
|
|
|
|
- if (useDirect) {
|
|
|
|
- dataBuf = directify(dataBuf);
|
|
|
|
- checksumBuf = directify(checksumBuf);
|
|
|
|
|
|
+
|
|
|
|
+ // Swap out for direct buffers if requested.
|
|
|
|
+ if (useDirect) {
|
|
|
|
+ dataBuf = directify(dataBuf);
|
|
|
|
+ checksumBuf = directify(checksumBuf);
|
|
|
|
+ }
|
|
}
|
|
}
|
|
-
|
|
|
|
- // calculate real checksum, make sure it passes
|
|
|
|
- checksum.calculateChunkedSums(dataBuf, checksumBuf);
|
|
|
|
- checksum.verifyChunkedSums(dataBuf, checksumBuf, "fake file", 0);
|
|
|
|
-
|
|
|
|
- // Change a byte in the header and in the trailer, make sure
|
|
|
|
- // it doesn't affect checksum result
|
|
|
|
- corruptBufferOffset(checksumBuf, 0);
|
|
|
|
- checksum.verifyChunkedSums(dataBuf, checksumBuf, "fake file", 0);
|
|
|
|
- corruptBufferOffset(dataBuf, 0);
|
|
|
|
- dataBuf.limit(dataBuf.limit() + 1);
|
|
|
|
- corruptBufferOffset(dataBuf, dataLength + DATA_OFFSET_IN_BUFFER);
|
|
|
|
- dataBuf.limit(dataBuf.limit() - 1);
|
|
|
|
- checksum.verifyChunkedSums(dataBuf, checksumBuf, "fake file", 0);
|
|
|
|
-
|
|
|
|
- // Make sure bad checksums fail - error at beginning of array
|
|
|
|
- corruptBufferOffset(checksumBuf, SUMS_OFFSET_IN_BUFFER);
|
|
|
|
- try {
|
|
|
|
|
|
+
|
|
|
|
+ void testCorrectness() throws ChecksumException {
|
|
|
|
+ // calculate real checksum, make sure it passes
|
|
|
|
+ checksum.calculateChunkedSums(dataBuf, checksumBuf);
|
|
checksum.verifyChunkedSums(dataBuf, checksumBuf, "fake file", 0);
|
|
checksum.verifyChunkedSums(dataBuf, checksumBuf, "fake file", 0);
|
|
- fail("Did not throw on bad checksums");
|
|
|
|
- } catch (ChecksumException ce) {
|
|
|
|
- assertEquals(0, ce.getPos());
|
|
|
|
- }
|
|
|
|
|
|
|
|
- // Make sure bad checksums fail - error at end of array
|
|
|
|
- uncorruptBufferOffset(checksumBuf, SUMS_OFFSET_IN_BUFFER);
|
|
|
|
- corruptBufferOffset(checksumBuf, SUMS_OFFSET_IN_BUFFER + sumsLength - 1);
|
|
|
|
- try {
|
|
|
|
|
|
+ // Change a byte in the header and in the trailer, make sure
|
|
|
|
+ // it doesn't affect checksum result
|
|
|
|
+ corruptBufferOffset(checksumBuf, 0);
|
|
|
|
+ checksum.verifyChunkedSums(dataBuf, checksumBuf, "fake file", 0);
|
|
|
|
+ corruptBufferOffset(dataBuf, 0);
|
|
|
|
+ dataBuf.limit(dataBuf.limit() + 1);
|
|
|
|
+ corruptBufferOffset(dataBuf, dataLength + DATA_OFFSET_IN_BUFFER);
|
|
|
|
+ dataBuf.limit(dataBuf.limit() - 1);
|
|
checksum.verifyChunkedSums(dataBuf, checksumBuf, "fake file", 0);
|
|
checksum.verifyChunkedSums(dataBuf, checksumBuf, "fake file", 0);
|
|
- fail("Did not throw on bad checksums");
|
|
|
|
- } catch (ChecksumException ce) {
|
|
|
|
- int expectedPos = checksum.getBytesPerChecksum() * (numSums - 1);
|
|
|
|
- assertEquals(expectedPos, ce.getPos());
|
|
|
|
- assertTrue(ce.getMessage().contains("fake file"));
|
|
|
|
|
|
+
|
|
|
|
+ // Make sure bad checksums fail - error at beginning of array
|
|
|
|
+ corruptBufferOffset(checksumBuf, SUMS_OFFSET_IN_BUFFER);
|
|
|
|
+ try {
|
|
|
|
+ checksum.verifyChunkedSums(dataBuf, checksumBuf, "fake file", 0);
|
|
|
|
+ fail("Did not throw on bad checksums");
|
|
|
|
+ } catch (ChecksumException ce) {
|
|
|
|
+ assertEquals(0, ce.getPos());
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Make sure bad checksums fail - error at end of array
|
|
|
|
+ uncorruptBufferOffset(checksumBuf, SUMS_OFFSET_IN_BUFFER);
|
|
|
|
+ corruptBufferOffset(checksumBuf, SUMS_OFFSET_IN_BUFFER + sumsLength - 1);
|
|
|
|
+ try {
|
|
|
|
+ checksum.verifyChunkedSums(dataBuf, checksumBuf, "fake file", 0);
|
|
|
|
+ fail("Did not throw on bad checksums");
|
|
|
|
+ } catch (ChecksumException ce) {
|
|
|
|
+ int expectedPos = checksum.getBytesPerChecksum() * (numSums - 1);
|
|
|
|
+ assertEquals(expectedPos, ce.getPos());
|
|
|
|
+ assertTrue(ce.getMessage().contains("fake file"));
|
|
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
|
|
+ private void doBulkTest(DataChecksum checksum, int dataLength,
|
|
|
|
+ boolean useDirect) throws Exception {
|
|
|
|
+ System.err.println("Testing bulk checksums of length " +
|
|
|
|
+ dataLength + " with " +
|
|
|
|
+ (useDirect ? "direct" : "array-backed") + " buffers");
|
|
|
|
+
|
|
|
|
+ new Harness(checksum, dataLength, useDirect).testCorrectness();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Simple performance test for the "common case" checksum usage in HDFS:
|
|
|
|
+ * computing and verifying CRC32C with 512 byte chunking on native
|
|
|
|
+ * buffers.
|
|
|
|
+ */
|
|
|
|
+ @Test
|
|
|
|
+ public void commonUsagePerfTest() throws Exception {
|
|
|
|
+ final int NUM_RUNS = 5;
|
|
|
|
+ final DataChecksum checksum = DataChecksum.newDataChecksum(DataChecksum.Type.CRC32C, 512);
|
|
|
|
+ final int dataLength = 512 * 1024 * 1024;
|
|
|
|
+ Harness h = new Harness(checksum, dataLength, true);
|
|
|
|
+
|
|
|
|
+ for (int i = 0; i < NUM_RUNS; i++) {
|
|
|
|
+ Stopwatch s = new Stopwatch().start();
|
|
|
|
+ // calculate real checksum, make sure it passes
|
|
|
|
+ checksum.calculateChunkedSums(h.dataBuf, h.checksumBuf);
|
|
|
|
+ s.stop();
|
|
|
|
+ System.err.println("Calculate run #" + i + ": " +
|
|
|
|
+ s.elapsedTime(TimeUnit.MICROSECONDS) + "us");
|
|
|
|
+
|
|
|
|
+ s = new Stopwatch().start();
|
|
|
|
+ // calculate real checksum, make sure it passes
|
|
|
|
+ checksum.verifyChunkedSums(h.dataBuf, h.checksumBuf, "fake file", 0);
|
|
|
|
+ s.stop();
|
|
|
|
+ System.err.println("Verify run #" + i + ": " +
|
|
|
|
+ s.elapsedTime(TimeUnit.MICROSECONDS) + "us");
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
@Test
|
|
@Test
|
|
public void testEquality() {
|
|
public void testEquality() {
|
|
assertEquals(
|
|
assertEquals(
|