Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
e294940
HDDS-11763. Implement container repair logic within datanodes
aswinshakil Nov 22, 2024
99de480
HDDS-11763. Added test cases and fixed bugs.
aswinshakil Jan 6, 2025
f8606fe
HDDS-11763. Fix tests.
aswinshakil Jan 8, 2025
52bde04
Merge branch 'HDDS-10239-container-reconciliation' of https://github.…
aswinshakil Jan 24, 2025
385e8c1
Address partial review comments.
aswinshakil Jan 25, 2025
04aa8a0
Address review comments.
aswinshakil Jan 31, 2025
4f291a0
Merge branch 'HDDS-10239-container-reconciliation' into HDDS-11763-re…
aswinshakil Feb 10, 2025
112762d
Address review.
aswinshakil Feb 18, 2025
07c40e4
Fix tests.
aswinshakil Feb 19, 2025
0350c29
Merge branch 'HDDS-10239-container-reconciliation' of https://github.…
aswinshakil Feb 19, 2025
aa34c57
Address Review Comments
aswinshakil Feb 21, 2025
1e5685e
Merge branch 'HDDS-10239-container-reconciliation' of https://github.…
aswinshakil Mar 7, 2025
546bd6f
Add unit test suite
aswinshakil Mar 10, 2025
89a9848
Fix findbugs.
aswinshakil Mar 10, 2025
2b272e7
Fix findbugs.
aswinshakil Mar 10, 2025
7570005
Address review comments.
aswinshakil Mar 18, 2025
47b5fef
Use BlockInputStream to read data.
aswinshakil Mar 20, 2025
caffe21
Fix findbugs
aswinshakil Mar 20, 2025
12b9443
Use existing blockData from BlockInputStream
aswinshakil Mar 20, 2025
d03e4d9
Use ByteBuffer instead of byte array.
aswinshakil Mar 24, 2025
369b24d
Address review comments.
aswinshakil Apr 4, 2025
f062bed
Address review comments.
aswinshakil Apr 4, 2025
a5796a1
Update tests and address review comments.
aswinshakil Apr 8, 2025
d578f3c
Address review comments.
aswinshakil Apr 10, 2025
130d57a
Address review comments.
aswinshakil Apr 10, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ public XceiverClientManager getXceiverClientManager() {
return xceiverClientManager;
}

public TokenHelper getTokenHelper() {
return tokenHelper;
}

/**
* Reads {@link ContainerProtos.ContainerChecksumInfo} for a specified container for the specified datanode.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.function.Function;
import java.util.stream.Collectors;

import com.google.common.util.concurrent.Striped;
import org.apache.hadoop.fs.FileUtil;
Expand All @@ -54,24 +55,27 @@
import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos.WriteChunkRequestProto;
import org.apache.hadoop.hdds.scm.ByteStringConversion;
import org.apache.hadoop.hdds.scm.ScmConfigKeys;
import org.apache.hadoop.hdds.scm.XceiverClientSpi;
import org.apache.hadoop.hdds.scm.container.common.helpers.StorageContainerException;
import org.apache.hadoop.hdds.scm.storage.ContainerProtocolCalls;
import org.apache.hadoop.hdds.security.token.OzoneBlockTokenIdentifier;
import org.apache.hadoop.hdds.upgrade.HDDSLayoutFeature;
import org.apache.hadoop.hdds.utils.FaultInjector;
import org.apache.hadoop.hdds.utils.HddsServerUtil;
import org.apache.hadoop.ozone.OzoneConfigKeys;
import org.apache.hadoop.ozone.common.Checksum;
import org.apache.hadoop.ozone.common.ChecksumByteBuffer;
import org.apache.hadoop.ozone.common.ChecksumByteBufferFactory;
import org.apache.hadoop.ozone.common.ChunkBuffer;
import org.apache.hadoop.ozone.common.OzoneChecksumException;
import org.apache.hadoop.ozone.common.utils.BufferUtils;
import org.apache.hadoop.ozone.container.checksum.ContainerChecksumTreeManager;
import org.apache.hadoop.ozone.container.checksum.ContainerDiffReport;
import org.apache.hadoop.ozone.container.checksum.DNContainerOperationClient;
import org.apache.hadoop.ozone.container.checksum.ContainerMerkleTree;
import org.apache.hadoop.ozone.container.common.helpers.BlockData;
import org.apache.hadoop.ozone.container.common.helpers.ChunkInfo;
import org.apache.hadoop.ozone.container.common.helpers.ContainerMetrics;
import org.apache.hadoop.ozone.container.common.helpers.ContainerUtils;
import org.apache.hadoop.ozone.container.common.helpers.TokenHelper;
import org.apache.hadoop.ozone.container.common.impl.ContainerLayoutVersion;
import org.apache.hadoop.ozone.container.common.impl.ContainerData;
import org.apache.hadoop.ozone.container.common.impl.ContainerSet;
Expand Down Expand Up @@ -137,8 +141,12 @@

import org.apache.hadoop.ozone.container.common.interfaces.ScanResult;
import static org.apache.hadoop.ozone.ClientVersion.EC_REPLICA_INDEX_REQUIRED_IN_BLOCK_REQUEST;
import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_SCM_BLOCK_SIZE;
import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_SCM_BLOCK_SIZE_DEFAULT;
import static org.apache.hadoop.ozone.OzoneConsts.INCREMENTAL_CHUNK_LIST;
import static org.apache.hadoop.ozone.container.checksum.DNContainerOperationClient.createSingleNodePipeline;

import org.apache.hadoop.security.token.Token;
import org.apache.hadoop.util.Time;
import org.apache.ratis.statemachine.StateMachine;
import org.apache.ratis.thirdparty.com.google.protobuf.ByteString;
Expand Down Expand Up @@ -1427,21 +1435,132 @@ public void deleteContainer(Container container, boolean force)
@Override
public void reconcileContainer(DNContainerOperationClient dnClient, Container<?> container,
Set<DatanodeDetails> peers) throws IOException {
// TODO Just a deterministic placeholder hash for testing until actual implementation is finished.
ContainerData data = container.getContainerData();
long id = data.getContainerID();
ByteBuffer byteBuffer = ByteBuffer.allocate(Long.BYTES)
.putLong(id)
.asReadOnlyBuffer();
byteBuffer.rewind();
ChecksumByteBuffer checksumImpl = ChecksumByteBufferFactory.crc32Impl();
checksumImpl.update(byteBuffer);
long dataChecksum = checksumImpl.getValue();
LOG.info("Generated data checksum of container {} for testing: {}", id, dataChecksum);
data.setDataChecksum(dataChecksum);
KeyValueContainer kvContainer = (KeyValueContainer) container;
KeyValueContainerData containerData = (KeyValueContainerData) container.getContainerData();
Comment thread
kerneltime marked this conversation as resolved.

for (DatanodeDetails peer : peers) {
ContainerProtos.ContainerChecksumInfo peerChecksumInfo = dnClient.getContainerChecksumInfo(
containerData.getContainerID(), peer);
if (peerChecksumInfo == null) {
LOG.warn("Checksum not yet generated for peer: {}", peer);
Comment thread
aswinshakil marked this conversation as resolved.
Outdated
return;
Comment thread
aswinshakil marked this conversation as resolved.
Outdated
}

long scmBlockSize = (long) conf.getStorageSize(OZONE_SCM_BLOCK_SIZE, OZONE_SCM_BLOCK_SIZE_DEFAULT,
Comment thread
aswinshakil marked this conversation as resolved.
Outdated
StorageUnit.BYTES);
ContainerDiffReport diffReport = checksumManager.diff(containerData, peerChecksumInfo);
Comment thread
aswinshakil marked this conversation as resolved.
Outdated
TokenHelper tokenHelper = dnClient.getTokenHelper();
XceiverClientSpi xceiverClient = dnClient.getXceiverClientManager()
.acquireClient(createSingleNodePipeline(peer));

try {
// Handle missing blocks
for (ContainerProtos.BlockMerkleTree missingBlock : diffReport.getMissingBlocks()) {
handleMissingBlock(kvContainer, containerData, tokenHelper, scmBlockSize, xceiverClient, missingBlock);
}

// Handle missing chunks
for (Map.Entry<Long, List<ContainerProtos.ChunkMerkleTree>> entry : diffReport.getMissingChunks().entrySet()) {
reconcileChunk(kvContainer, containerData, tokenHelper, scmBlockSize, xceiverClient, entry);
Comment thread
aswinshakil marked this conversation as resolved.
Outdated
}

// Handle corrupt chunks
for (Map.Entry<Long, List<ContainerProtos.ChunkMerkleTree>> entry : diffReport.getCorruptChunks().entrySet()) {
reconcileChunk(kvContainer, containerData, tokenHelper, scmBlockSize, xceiverClient, entry);
}
} finally {
dnClient.getXceiverClientManager().releaseClient(xceiverClient, false);
}
}

Comment thread
errose28 marked this conversation as resolved.
updateContainerChecksum(containerData);
Comment thread
aswinshakil marked this conversation as resolved.
Outdated
long dataChecksum = updateContainerChecksum(containerData);
LOG.info("Checksum data for container {} is updated to {}", containerData.getContainerID(), dataChecksum);
Comment thread
aswinshakil marked this conversation as resolved.
Outdated
containerData.setDataChecksum(dataChecksum);
Comment thread
aswinshakil marked this conversation as resolved.
Outdated
sendICR(container);
}

private long updateContainerChecksum(KeyValueContainerData containerData) throws IOException {
Comment thread
aswinshakil marked this conversation as resolved.
Outdated
ContainerMerkleTree merkleTree = new ContainerMerkleTree();
try (DBHandle dbHandle = BlockUtils.getDB(containerData, conf);
BlockIterator<BlockData> blockIterator = dbHandle.getStore().
getBlockIterator(containerData.getContainerID())) {
while (blockIterator.hasNext()) {
BlockData blockData = blockIterator.nextBlock();
List<ContainerProtos.ChunkInfo> chunkInfos = blockData.getChunks();
merkleTree.addChunks(blockData.getLocalID(), chunkInfos);
Comment thread
errose28 marked this conversation as resolved.
}
}
checksumManager.writeContainerDataTree(containerData, merkleTree);
return merkleTree.toProto().getDataChecksum();
}

private void handleMissingBlock(KeyValueContainer container, ContainerData containerData, TokenHelper tokenHelper,
long scmBlockSize, XceiverClientSpi xceiverClient,
ContainerProtos.BlockMerkleTree missingBlock) throws IOException {
BlockID blockID = new BlockID(containerData.getContainerID(), missingBlock.getBlockID());
Token<OzoneBlockTokenIdentifier> blockToken = tokenHelper.getBlockToken(blockID, scmBlockSize);
// TODo: Cache the blockResponse to reuse it again.
ContainerProtos.GetBlockResponseProto blockResponse = ContainerProtocolCalls.getBlock(xceiverClient, blockID,
blockToken, new HashMap<>());
// TODO: Add BcsId in BlockMerkleTree to avoid this call
Comment thread
aswinshakil marked this conversation as resolved.
Outdated
ContainerProtos.GetCommittedBlockLengthResponseProto blockLengthResponse =
ContainerProtocolCalls.getCommittedBlockLength(xceiverClient, blockID, blockToken);
List<ContainerProtos.ChunkInfo> chunksList = blockResponse.getBlockData().getChunksList();

for (ContainerProtos.ChunkInfo chunkInfoProto : chunksList) {
ByteString chunkData = readChunkData(xceiverClient, chunkInfoProto, blockID, blockToken);
ChunkBuffer chunkBuffer = ChunkBuffer.wrap(chunkData.asReadOnlyByteBuffer());
writeChunkForClosedContainer(ChunkInfo.getFromProtoBuf(chunkInfoProto), blockID, chunkBuffer, container);
}

putBlockForClosedContainer(chunksList, container, BlockData.getFromProtoBuf(blockResponse.getBlockData()),
blockLengthResponse.getBlockLength());
}

private ByteString readChunkData(XceiverClientSpi xceiverClient, ContainerProtos.ChunkInfo chunkInfoProto,
BlockID blockID, Token<OzoneBlockTokenIdentifier> blockToken) throws IOException {
ContainerProtos.ReadChunkResponseProto response =
ContainerProtocolCalls.readChunk(xceiverClient, chunkInfoProto, blockID.getDatanodeBlockIDProtobuf(),
null, blockToken);
Comment thread
errose28 marked this conversation as resolved.
Outdated

if (response.hasData()) {
return response.getData();
} else if (response.hasDataBuffers()) {
return BufferUtils.concatByteStrings(response.getDataBuffers().getBuffersList());
} else {
throw new IOException("Error reading chunk data: No data returned.");
}
}

Comment thread
kerneltime marked this conversation as resolved.
private void reconcileChunk(KeyValueContainer container, ContainerData containerData, TokenHelper tokenHelper,
Comment thread
aswinshakil marked this conversation as resolved.
Outdated
long scmBlockSize, XceiverClientSpi xceiverClient,
Map.Entry<Long, List<ContainerProtos.ChunkMerkleTree>> mapEntry) throws IOException {
Comment thread
aswinshakil marked this conversation as resolved.
Outdated
long blockId = mapEntry.getKey();
List<ContainerProtos.ChunkMerkleTree> chunkList = mapEntry.getValue();
Set<Long> offsets = chunkList.stream().map(ContainerProtos.ChunkMerkleTree::getOffset)
.collect(Collectors.toSet());
BlockID blockID = new BlockID(containerData.getContainerID(), blockId);
Token<OzoneBlockTokenIdentifier> blockToken = tokenHelper.getBlockToken(blockID, scmBlockSize);
ContainerProtos.GetBlockResponseProto blockResponse = ContainerProtocolCalls.getBlock(xceiverClient, blockID,
blockToken, new HashMap<>());
// TODO: Add BcsId in BlockMerkleTree to avoid this call
ContainerProtos.GetCommittedBlockLengthResponseProto blockLengthResponse =
ContainerProtocolCalls.getCommittedBlockLength(xceiverClient, blockID, blockToken);
List<ContainerProtos.ChunkInfo> chunksList = blockResponse.getBlockData().getChunksList();

for (ContainerProtos.ChunkInfo chunkInfoProto : chunksList) {
if (offsets.contains(chunkInfoProto.getOffset())) {
Comment thread
aswinshakil marked this conversation as resolved.
Outdated
ByteString chunkData = readChunkData(xceiverClient, chunkInfoProto, blockID, blockToken);
ChunkBuffer chunkBuffer = ChunkBuffer.wrap(chunkData.asReadOnlyByteBuffer());
writeChunkForClosedContainer(ChunkInfo.getFromProtoBuf(chunkInfoProto), blockID, chunkBuffer, container);
}
}

putBlockForClosedContainer(chunksList, container, BlockData.getFromProtoBuf(blockResponse.getBlockData()),
blockLengthResponse.getBlockLength());
}

/**
* Called by BlockDeletingService to delete all the chunks in a block
* before proceeding to delete the block info from DB.
Expand Down