aswinshakil commented on code in PR #7293:
URL: https://github.com/apache/ozone/pull/7293#discussion_r1847116700
##########
hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/checksum/ContainerMerkleTreeTestUtils.java:
##########
@@ -128,24 +138,196 @@ public static ContainerProtos.ContainerChecksumInfo
readChecksumFile(ContainerDa
* structure is preserved throughout serialization, deserialization, and API
calls.
*/
public static ContainerMerkleTree buildTestTree(ConfigurationSource conf) {
- final long blockID1 = 1;
- final long blockID2 = 2;
- final long blockID3 = 3;
- ContainerProtos.ChunkInfo b1c1 = buildChunk(conf, 0, ByteBuffer.wrap(new
byte[]{1, 2, 3}));
- ContainerProtos.ChunkInfo b1c2 = buildChunk(conf, 1, ByteBuffer.wrap(new
byte[]{4, 5, 6}));
- ContainerProtos.ChunkInfo b2c1 = buildChunk(conf, 0, ByteBuffer.wrap(new
byte[]{7, 8, 9}));
- ContainerProtos.ChunkInfo b2c2 = buildChunk(conf, 1, ByteBuffer.wrap(new
byte[]{12, 11, 10}));
- ContainerProtos.ChunkInfo b3c1 = buildChunk(conf, 0, ByteBuffer.wrap(new
byte[]{13, 14, 15}));
- ContainerProtos.ChunkInfo b3c2 = buildChunk(conf, 1, ByteBuffer.wrap(new
byte[]{16, 17, 18}));
-
ContainerMerkleTree tree = new ContainerMerkleTree();
- tree.addChunks(blockID1, Arrays.asList(b1c1, b1c2));
- tree.addChunks(blockID2, Arrays.asList(b2c1, b2c2));
- tree.addChunks(blockID3, Arrays.asList(b3c1, b3c2));
-
+ byte byteValue = 1;
+ for (int blockIndex = 1; blockIndex <= 5; blockIndex++) {
+ List<ContainerProtos.ChunkInfo> chunks = new ArrayList<>();
+ for (int chunkIndex = 0; chunkIndex < 4; chunkIndex++) {
+ chunks.add(buildChunk(conf, chunkIndex, ByteBuffer.wrap(new
byte[]{byteValue++, byteValue++, byteValue++})));
+ }
+ tree.addChunks(blockIndex, chunks);
+ }
return tree;
}
+ /**
+ * Returns a Pair of merkle tree and the expected container diff for that
merkle tree.
+ */
+ public static Pair<ContainerProtos.ContainerMerkleTree,
ContainerChecksumTreeManager.ContainerDiff>
+ buildTestTreeWithMismatches(ContainerMerkleTree originalTree, int
numMissingBlocks, int numMissingChunks,
+ int numCorruptChunks) {
+
+ ContainerProtos.ContainerMerkleTree.Builder treeBuilder =
originalTree.toProto().toBuilder();
+ ContainerChecksumTreeManager.ContainerDiff diff = new
ContainerChecksumTreeManager.ContainerDiff();
+
+ introduceMissingBlocks(treeBuilder, numMissingBlocks, diff);
+ introduceMissingChunks(treeBuilder, numMissingChunks, diff);
+ introduceCorruptChunks(treeBuilder, numCorruptChunks, diff);
+ ContainerProtos.ContainerMerkleTree build = treeBuilder.build();
+ return Pair.of(build, diff);
+ }
+
+ /**
+ * Introduces missing blocks by removing random blocks from the tree.
+ */
+ private static void
introduceMissingBlocks(ContainerProtos.ContainerMerkleTree.Builder treeBuilder,
+ int numMissingBlocks,
+
ContainerChecksumTreeManager.ContainerDiff diff) {
+ // Set to track unique blocks selected for mismatches
+ Set<Integer> selectedBlocks = new HashSet<>();
+ Random random = new Random();
+ for (int i = 0; i < numMissingBlocks; i++) {
+ int randomBlockIndex;
+ do {
+ randomBlockIndex =
random.nextInt(treeBuilder.getBlockMerkleTreeCount());
+ } while (selectedBlocks.contains(randomBlockIndex));
+ selectedBlocks.add(randomBlockIndex);
+ ContainerProtos.BlockMerkleTree blockMerkleTree =
treeBuilder.getBlockMerkleTree(randomBlockIndex);
+ diff.addMissingBlock(blockMerkleTree);
+ treeBuilder.removeBlockMerkleTree(randomBlockIndex);
+ treeBuilder.setDataChecksum(random.nextLong());
+ }
+ }
+
+ /**
+ * Introduces missing chunks by removing random chunks from selected blocks.
+ */
+ private static void
introduceMissingChunks(ContainerProtos.ContainerMerkleTree.Builder treeBuilder,
+ int numMissingChunks,
+
ContainerChecksumTreeManager.ContainerDiff diff) {
+ // Set to track unique blocks selected for mismatches
+ Random random = new Random();
+ for (int i = 0; i < numMissingChunks; i++) {
+ int randomBlockIndex =
random.nextInt(treeBuilder.getBlockMerkleTreeCount());
+
+ // Work on the chosen block to remove a random chunk
+ ContainerProtos.BlockMerkleTree.Builder blockBuilder =
treeBuilder.getBlockMerkleTreeBuilder(randomBlockIndex);
+ if (blockBuilder.getChunkMerkleTreeCount() > 0) {
+ int randomChunkIndex =
random.nextInt(blockBuilder.getChunkMerkleTreeCount());
+ ContainerProtos.ChunkMerkleTree chunkMerkleTree =
blockBuilder.getChunkMerkleTree(randomChunkIndex);
+ diff.addMissingChunk(blockBuilder.getBlockID(), chunkMerkleTree);
+ blockBuilder.removeChunkMerkleTree(randomChunkIndex);
+ blockBuilder.setBlockChecksum(random.nextLong());
+ treeBuilder.setDataChecksum(random.nextLong());
+ }
+ }
+ }
+
+ /**
+ * Introduces corrupt chunks by altering the checksum and setting them as
unhealthy,
+ * ensuring each chunk in a block is only selected once for corruption.
+ */
+ private static void
introduceCorruptChunks(ContainerProtos.ContainerMerkleTree.Builder treeBuilder,
+ int numCorruptChunks,
+
ContainerChecksumTreeManager.ContainerDiff diff) {
+ Map<Integer, Set<Integer>> corruptedChunksByBlock = new HashMap<>();
+ Random random = new Random();
+
+ for (int i = 0; i < numCorruptChunks; i++) {
+ // Select a random block
+ int randomBlockIndex =
random.nextInt(treeBuilder.getBlockMerkleTreeCount());
+ ContainerProtos.BlockMerkleTree.Builder blockBuilder =
treeBuilder.getBlockMerkleTreeBuilder(randomBlockIndex);
+
+ // Ensure each chunk in the block is only corrupted once
+ Set<Integer> corruptedChunks =
corruptedChunksByBlock.computeIfAbsent(randomBlockIndex, k -> new HashSet<>());
+ if (corruptedChunks.size() < blockBuilder.getChunkMerkleTreeCount()) {
+ int randomChunkIndex;
+ do {
+ randomChunkIndex =
random.nextInt(blockBuilder.getChunkMerkleTreeCount());
+ } while (corruptedChunks.contains(randomChunkIndex));
+ corruptedChunks.add(randomChunkIndex);
+
+ // Corrupt the selected chunk
+ ContainerProtos.ChunkMerkleTree.Builder chunkBuilder =
blockBuilder.getChunkMerkleTreeBuilder(randomChunkIndex);
+ diff.addCorruptChunk(blockBuilder.getBlockID(), chunkBuilder.build());
+ chunkBuilder.setChunkChecksum(chunkBuilder.getChunkChecksum() +
random.nextInt(1000) + 1);
+ chunkBuilder.setIsHealthy(false);
+ blockBuilder.setBlockChecksum(random.nextLong());
+ treeBuilder.setDataChecksum(random.nextLong());
+ }
+ }
+ }
+
+ public static void
assertContainerDiffMatch(ContainerChecksumTreeManager.ContainerDiff
expectedDiff,
+ ContainerChecksumTreeManager.ContainerDiff actualDiff) {
+ assertNotNull(expectedDiff, "Expected diff is null");
+ assertNotNull(actualDiff, "Actual diff is null");
+ assertEquals(expectedDiff.getMissingBlocks().size(),
actualDiff.getMissingBlocks().size(),
+ "Mismatch in number of missing blocks");
+ assertEquals(expectedDiff.getMissingChunks().size(),
actualDiff.getMissingChunks().size(),
+ "Mismatch in number of missing chunks");
+ assertEquals(expectedDiff.getCorruptChunks().size(),
actualDiff.getCorruptChunks().size(),
+ "Mismatch in number of corrupt chunks");
+
+ List<ContainerProtos.BlockMerkleTree> expectedMissingBlocks =
expectedDiff.getMissingBlocks().stream().sorted(
+
Comparator.comparing(ContainerProtos.BlockMerkleTree::getBlockID)).collect(Collectors.toList());
+ List<ContainerProtos.BlockMerkleTree> actualMissingBlocks =
expectedDiff.getMissingBlocks().stream().sorted(
+
Comparator.comparing(ContainerProtos.BlockMerkleTree::getBlockID)).collect(Collectors.toList());
+ for (int i = 0; i < expectedMissingBlocks.size(); i++) {
+ ContainerProtos.BlockMerkleTree expectedBlockMerkleTree =
expectedMissingBlocks.get(i);
+ ContainerProtos.BlockMerkleTree actualBlockMerkleTree =
actualMissingBlocks.get(i);
+ assertEquals(expectedBlockMerkleTree.getBlockID(),
actualBlockMerkleTree.getBlockID());
+ assertEquals(expectedBlockMerkleTree.getChunkMerkleTreeCount(),
+ actualBlockMerkleTree.getChunkMerkleTreeCount());
+ assertEquals(expectedBlockMerkleTree.getBlockChecksum(),
actualBlockMerkleTree.getBlockChecksum());
+
assertEqualsChunkMerkleTree(expectedBlockMerkleTree.getChunkMerkleTreeList(),
+ actualBlockMerkleTree.getChunkMerkleTreeList());
+ }
+
+ // Check missing chunks
+ Map<Long, List<ContainerProtos.ChunkMerkleTree>> expectedMissingChunks =
expectedDiff.getMissingChunks();
+ Map<Long, List<ContainerProtos.ChunkMerkleTree>> actualMissingChunks =
actualDiff.getMissingChunks();
+
+ for (Map.Entry<Long, List<ContainerProtos.ChunkMerkleTree>> entry :
expectedMissingChunks.entrySet()) {
+ Long blockId = entry.getKey();
+ List<ContainerProtos.ChunkMerkleTree> expectedChunks =
entry.getValue().stream().sorted(
+
Comparator.comparing(ContainerProtos.ChunkMerkleTree::getOffset)).collect(Collectors.toList());
+ List<ContainerProtos.ChunkMerkleTree> actualChunks =
actualMissingChunks.get(blockId).stream().sorted(
+
Comparator.comparing(ContainerProtos.ChunkMerkleTree::getOffset)).collect(Collectors.toList());
+
+ assertNotNull(actualChunks, "Missing chunks for block " + blockId + "
not found in actual diff");
+ assertEquals(expectedChunks.size(), actualChunks.size(),
+ "Mismatch in number of missing chunks for block " + blockId);
+ assertEqualsChunkMerkleTree(expectedChunks, actualChunks);
+ for (int i = 0; i < expectedChunks.size(); i++) {
+ ContainerProtos.ChunkMerkleTree expectedChunk = expectedChunks.get(i);
+ ContainerProtos.ChunkMerkleTree actualChunk = actualChunks.get(i);
+ assertEquals(expectedChunk.getOffset(), actualChunk.getOffset(),
+ "Mismatch in chunk offset for block " + blockId);
+ assertEquals(expectedChunk.getChunkChecksum(),
actualChunk.getChunkChecksum(),
+ "Mismatch in chunk checksum for block " + blockId);
+ }
Review Comment:
Nice catch , I missed removing it. I updated it with better log messages.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]