This is an automated email from the ASF dual-hosted git repository.
devmadhuu pushed a commit to branch HDDS-11233
in repository https://gitbox.apache.org/repos/asf/ozone.git
The following commit(s) were added to refs/heads/HDDS-11233 by this push:
new 88dc181fdaf HDDS-16388. Datanode putBlock Support StorageType (#11223)
88dc181fdaf is described below
commit 88dc181fdafa88d6afc1c2200894a27611d1cae2
Author: XiChen <[email protected]>
AuthorDate: Thu Oct 1 12:17:49 2026 +0800
HDDS-16388. Datanode putBlock Support StorageType (#11223)
---
.../hadoop/hdds/scm/storage/BlockOutputStream.java | 5 +-
.../java/org/apache/hadoop/hdds/HddsUtils.java | 5 +-
.../org/apache/hadoop/hdds/client/BlockID.java | 46 +++++--
.../hadoop/hdds/client/StorageTypeUtils.java | 8 ++
.../hdds/scm/storage/ContainerProtocolCalls.java | 1 +
.../ozone/container/ContainerTestHelper.java | 9 +-
.../container/common/impl/HddsDispatcher.java | 16 +++
.../server/ratis/ContainerStateMachine.java | 3 +-
.../ozone/container/keyvalue/KeyValueHandler.java | 26 +++-
.../container/keyvalue/helpers/BlockUtils.java | 19 +++
.../container/keyvalue/impl/BlockManagerImpl.java | 1 +
.../container/common/helpers/TestBlockData.java | 2 +-
.../common/impl/TestContainerPersistence.java | 101 ++++++++++++--
.../container/common/impl/TestHddsDispatcher.java | 147 ++++++++++++++++++++-
14 files changed, 357 insertions(+), 32 deletions(-)
diff --git
a/hadoop-hdds/client/src/main/java/org/apache/hadoop/hdds/scm/storage/BlockOutputStream.java
b/hadoop-hdds/client/src/main/java/org/apache/hadoop/hdds/scm/storage/BlockOutputStream.java
index 4d09af278b1..744b1dda667 100644
---
a/hadoop-hdds/client/src/main/java/org/apache/hadoop/hdds/scm/storage/BlockOutputStream.java
+++
b/hadoop-hdds/client/src/main/java/org/apache/hadoop/hdds/scm/storage/BlockOutputStream.java
@@ -41,7 +41,9 @@
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;
+import org.apache.hadoop.fs.StorageType;
import org.apache.hadoop.hdds.client.BlockID;
+import org.apache.hadoop.hdds.client.StorageTypeUtils;
import org.apache.hadoop.hdds.protocol.DatanodeDetails;
import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos;
import
org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos.BlockData;
@@ -186,7 +188,6 @@ public BlockOutputStream(
replicationIndex = pipeline.getReplicaIndex(pipeline.getClosestNode());
KeyValue keyValue =
KeyValue.newBuilder().setKey("TYPE").setValue("KEY").build();
-
ContainerProtos.DatanodeBlockID.Builder blkIDBuilder =
ContainerProtos.DatanodeBlockID.newBuilder()
.setContainerID(blockID.getContainerID())
@@ -195,6 +196,8 @@ public BlockOutputStream(
if (replicationIndex > 0) {
blkIDBuilder.setReplicaIndex(replicationIndex);
}
+ // TODO: Replica to the method parameter
+ blkIDBuilder.setStorageTypeID(StorageTypeUtils.getID(StorageType.DISK));
this.containerBlockData = BlockData.newBuilder().setBlockID(
blkIDBuilder.build()).addMetadata(keyValue);
this.pipeline = pipeline;
diff --git
a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/HddsUtils.java
b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/HddsUtils.java
index b4bebeb8b27..6ada29bf338 100644
--- a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/HddsUtils.java
+++ b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/HddsUtils.java
@@ -507,7 +507,8 @@ public static boolean requireContainerToken(Type cmdType) {
* @param msg container command
* @return block ID.
*/
- public static BlockID getBlockID(ContainerCommandRequestProtoOrBuilder msg) {
+ public static BlockID getBlockID(ContainerCommandRequestProtoOrBuilder msg)
throws
+ IllegalArgumentException {
DatanodeBlockID blockID = null;
switch (msg.getCmdType()) {
case DeleteBlock:
@@ -561,6 +562,8 @@ public static BlockID
getBlockID(ContainerCommandRequestProtoOrBuilder msg) {
}
break;
case WriteChunk:
+ case StreamInit:
+ case StreamInitWithPutBlock:
if (msg.hasWriteChunk()) {
blockID = msg.getWriteChunk().getBlockID();
}
diff --git
a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/client/BlockID.java
b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/client/BlockID.java
index 7141a65306d..c2a9cc11a1e 100644
---
a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/client/BlockID.java
+++
b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/client/BlockID.java
@@ -19,6 +19,7 @@
import com.fasterxml.jackson.annotation.JsonIgnore;
import java.util.Objects;
+import org.apache.hadoop.fs.StorageType;
import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos;
import org.apache.hadoop.hdds.protocol.proto.HddsProtos;
@@ -34,29 +35,42 @@ public class BlockID {
// BlockID object.
private final Integer replicaIndex;
+ // Represents storage type of Block on a particular Datanode.
+ // Note this variable in the OM side will be null,
+ // because the OmKeyLocationInfo#getProtobuf Use BlockID#getProtobuf to get
Protobuf object,
+ // BlockID#getProtobuf does not set storageType value in Protobuf.
+
+ // Currently for java class BlockID, OM and Datanode share the same java
class object,
+ // but the protobuf objects HddsProtos.BlockID and
ContainerProtos.DatanodeBlockID are
+ // used for OM and Datanode respectively.
+ private StorageType storageType;
+
public BlockID(long containerID, long localID) {
- this(containerID, localID, 0, null);
+ this(containerID, localID, 0, null, null);
}
- private BlockID(long containerID, long localID, long bcsID, Integer
repIndex) {
+ private BlockID(long containerID, long localID, long bcsID, Integer repIndex,
+ StorageType storageType) {
containerBlockID = new ContainerBlockID(containerID, localID);
blockCommitSequenceId = bcsID;
this.replicaIndex = repIndex;
+ this.storageType = storageType;
}
public BlockID(BlockID blockID) {
this(blockID.getContainerID(), blockID.getLocalID(),
blockID.getBlockCommitSequenceId(),
- blockID.getReplicaIndex());
+ blockID.getReplicaIndex(), blockID.getStorageType());
}
public BlockID(ContainerBlockID containerBlockID) {
- this(containerBlockID, 0, null);
+ this(containerBlockID, 0, null, null);
}
- private BlockID(ContainerBlockID containerBlockID, long bcsId, Integer
repIndex) {
+ private BlockID(ContainerBlockID containerBlockID, long bcsId, Integer
repIndex, StorageType storageType) {
this.containerBlockID = containerBlockID;
blockCommitSequenceId = bcsId;
this.replicaIndex = repIndex;
+ this.storageType = storageType;
}
public long getContainerID() {
@@ -84,6 +98,10 @@ public ContainerBlockID getContainerBlockID() {
return containerBlockID;
}
+ public StorageType getStorageType() {
+ return storageType;
+ }
+
@Override
public String toString() {
StringBuilder sb = new StringBuilder(64);
@@ -94,7 +112,8 @@ public String toString() {
public void appendTo(StringBuilder sb) {
containerBlockID.appendTo(sb);
sb.append(" bcsId: ").append(blockCommitSequenceId)
- .append(" replicaIndex: ").append(replicaIndex);
+ .append(" replicaIndex: ").append(replicaIndex)
+ .append(" storageType: ").append(storageType);
}
@JsonIgnore
@@ -103,6 +122,9 @@ public ContainerProtos.DatanodeBlockID
getDatanodeBlockIDProtobuf() {
if (replicaIndex != null) {
blockID.setReplicaIndex(replicaIndex);
}
+ if (storageType != null) {
+ blockID.setStorageTypeID(StorageTypeUtils.getID(storageType));
+ }
return blockID.build();
}
@@ -115,11 +137,17 @@ public ContainerProtos.DatanodeBlockID.Builder
getDatanodeBlockIDProtobufBuilder
}
@JsonIgnore
- public static BlockID getFromProtobuf(ContainerProtos.DatanodeBlockID
blockID) {
+ public static BlockID getFromProtobuf(ContainerProtos.DatanodeBlockID
blockID) throws
+ IllegalArgumentException {
+ StorageType storageType = null;
+ if (blockID.hasStorageTypeID() && blockID.getStorageTypeID() > 0) {
+ storageType =
StorageTypeUtils.getStorageTypeFromID(blockID.getStorageTypeID());
+ }
return new BlockID(blockID.getContainerID(),
blockID.getLocalID(),
blockID.getBlockCommitSequenceId(),
- blockID.hasReplicaIndex() ? blockID.getReplicaIndex() : null);
+ blockID.hasReplicaIndex() ? blockID.getReplicaIndex() : null,
+ storageType);
}
@JsonIgnore
@@ -133,7 +161,7 @@ public HddsProtos.BlockID getProtobuf() {
public static BlockID getFromProtobuf(HddsProtos.BlockID blockID) {
return new BlockID(
ContainerBlockID.getFromProtobuf(blockID.getContainerBlockID()),
- blockID.getBlockCommitSequenceId(), null);
+ blockID.getBlockCommitSequenceId(), null, null);
}
@Override
diff --git
a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/client/StorageTypeUtils.java
b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/client/StorageTypeUtils.java
index 6b94d16e57b..0b56ccd55a2 100644
---
a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/client/StorageTypeUtils.java
+++
b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/client/StorageTypeUtils.java
@@ -103,5 +103,13 @@ public static int getIDFromProtobuf(@Nonnull
HddsProtos.StorageTypeProto proto)
throw new IllegalArgumentException("Illegal Storage Type specified");
}
}
+
+ /**
+ * Returns integer representation of FileSystem StorageType.
+ * @return storageType int ID value
+ */
+ public static int getID(StorageType storageType) throws
IllegalArgumentException {
+ return getStorageTypeProto(storageType).getNumber();
+ }
}
diff --git
a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/scm/storage/ContainerProtocolCalls.java
b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/scm/storage/ContainerProtocolCalls.java
index 95cc58effaf..00316f739e5 100644
---
a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/scm/storage/ContainerProtocolCalls.java
+++
b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/scm/storage/ContainerProtocolCalls.java
@@ -636,6 +636,7 @@ public static PutSmallFileResponseProto writeSmallFile(
public static void createRecoveringContainer(XceiverClientSpi client,
long containerID, String encodedToken, int replicaIndex)
throws IOException {
+ // TODO Support for EC container recovery for specific storage type
createContainer(client, containerID, encodedToken,
ContainerProtos.ContainerDataProto.State.RECOVERING, replicaIndex);
}
diff --git
a/hadoop-hdds/common/src/test/java/org/apache/hadoop/ozone/container/ContainerTestHelper.java
b/hadoop-hdds/common/src/test/java/org/apache/hadoop/ozone/container/ContainerTestHelper.java
index c2813602ede..96f585a0b52 100644
---
a/hadoop-hdds/common/src/test/java/org/apache/hadoop/ozone/container/ContainerTestHelper.java
+++
b/hadoop-hdds/common/src/test/java/org/apache/hadoop/ozone/container/ContainerTestHelper.java
@@ -39,7 +39,9 @@
import java.util.concurrent.ThreadLocalRandom;
import org.apache.commons.io.IOUtils;
import org.apache.hadoop.conf.StorageUnit;
+import org.apache.hadoop.fs.StorageType;
import org.apache.hadoop.hdds.client.BlockID;
+import org.apache.hadoop.hdds.client.StorageTypeUtils;
import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos;
import
org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos.ChecksumType;
import
org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos.ContainerCommandRequestProto;
@@ -572,15 +574,18 @@ public static
ContainerProtos.ContainerCommandRequestProto getFinalizeBlockReque
}
public static BlockID getTestBlockID(long containerID) {
- return getTestBlockID(containerID, null);
+ return getTestBlockID(containerID, null, null);
}
- public static BlockID getTestBlockID(long containerID, Integer replicaIndex)
{
+ public static BlockID getTestBlockID(long containerID, Integer replicaIndex,
StorageType storageType) {
DatanodeBlockID.Builder datanodeBlockID =
DatanodeBlockID.newBuilder().setContainerID(containerID)
.setLocalID(UniqueId.next());
if (replicaIndex != null) {
datanodeBlockID.setReplicaIndex(replicaIndex);
}
+ if (storageType != null) {
+ datanodeBlockID.setStorageTypeID(StorageTypeUtils.getID(storageType));
+ }
return BlockID.getFromProtobuf(datanodeBlockID.build());
}
diff --git
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/impl/HddsDispatcher.java
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/impl/HddsDispatcher.java
index f1122a2debf..c8fa6b2a242 100644
---
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/impl/HddsDispatcher.java
+++
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/impl/HddsDispatcher.java
@@ -177,6 +177,7 @@ private boolean canIgnoreException(Result result) {
case DELETE_ON_OPEN_CONTAINER:
case UNSUPPORTED_REQUEST:// Blame client for sending unsupported request.
case MALFORMED_REQUEST:// Blame client for sending malformed request.
+ case INVALID_ARGUMENT:
case CONTAINER_MISSING:
case CONTAINER_ALREADY_EXISTS:
return true;
@@ -194,6 +195,11 @@ public void buildMissingContainerSetAndValidate(
@Override
public ContainerCommandResponseProto dispatch(
ContainerCommandRequestProto msg, DispatcherContext dispatcherContext) {
+ try {
+ validateBlockID(msg);
+ } catch (StorageContainerException e) {
+ return ContainerUtils.logAndReturnError(LOG, e, msg);
+ }
try {
return dispatcher.processRequest(msg,
req -> dispatchRequest(msg, dispatcherContext),
@@ -556,6 +562,15 @@ private void validateToken(
);
}
+ private static void validateBlockID(ContainerCommandRequestProto msg)
+ throws StorageContainerException {
+ try {
+ HddsUtils.getBlockID(msg);
+ } catch (IllegalArgumentException e) {
+ throw new StorageContainerException(e.getMessage(), e,
Result.INVALID_ARGUMENT);
+ }
+ }
+
/**
* This will be called as a part of creating the log entry during
* startTransaction in Ratis on the leader node. In such cases, if the
@@ -568,6 +583,7 @@ private void validateToken(
@Override
public void validateContainerCommand(
ContainerCommandRequestProto msg) throws StorageContainerException {
+ validateBlockID(msg);
try {
validateToken(msg);
} catch (IOException ioe) {
diff --git
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/transport/server/ratis/ContainerStateMachine.java
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/transport/server/ratis/ContainerStateMachine.java
index 9a3e988e27a..441f282e348 100644
---
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/transport/server/ratis/ContainerStateMachine.java
+++
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/transport/server/ratis/ContainerStateMachine.java
@@ -1188,7 +1188,8 @@ public CompletableFuture<Message>
applyTransaction(TransactionContext trx) {
if (r.getResult() != ContainerProtos.Result.SUCCESS
&& r.getResult() != ContainerProtos.Result.CONTAINER_NOT_OPEN
&& r.getResult() != ContainerProtos.Result.CLOSED_CONTAINER_IO
- && r.getResult() !=
ContainerProtos.Result.CHUNK_FILE_INCONSISTENCY) {
+ && r.getResult() != ContainerProtos.Result.CHUNK_FILE_INCONSISTENCY
+ && r.getResult() != ContainerProtos.Result.INVALID_ARGUMENT) {
StorageContainerException sce =
new StorageContainerException(r.getMessage(), r.getResult());
LOG.error(
diff --git
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/keyvalue/KeyValueHandler.java
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/keyvalue/KeyValueHandler.java
index 693b33ab53d..78871853538 100644
---
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/keyvalue/KeyValueHandler.java
+++
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/keyvalue/KeyValueHandler.java
@@ -295,6 +295,7 @@ public StateMachine.DataChannel getStreamDataChannel(
if (msg.hasWriteChunk()) {
BlockID blockID =
BlockID.getFromProtobuf(msg.getWriteChunk().getBlockID());
+ BlockUtils.verifyStorageType(kvContainer.getContainerData(), blockID);
return chunkManager.getStreamDataChannel(kvContainer,
blockID, putBlock, metrics);
@@ -692,6 +693,8 @@ ContainerCommandResponseProto handlePutBlock(
ContainerProtos.BlockData data = request.getPutBlock().getBlockData();
BlockData blockData = BlockData.getFromProtoBuf(data);
Objects.requireNonNull(blockData, "blockData == null");
+ Objects.requireNonNull(blockData.getBlockID());
+ BlockUtils.verifyStorageType(kvContainer.getContainerData(),
blockData.getBlockID());
boolean endOfBlock = false;
if (!request.getPutBlock().hasEof() || request.getPutBlock().getEof()) {
@@ -1114,6 +1117,8 @@ ContainerCommandResponseProto handleWriteChunk(
WriteChunkRequestProto writeChunk = request.getWriteChunk();
BlockID blockID = BlockID.getFromProtobuf(writeChunk.getBlockID());
+ BlockUtils.verifyStorageType(kvContainer.getContainerData(), blockID);
+ BlockData blockData = validateWriteChunkBlockData(writeChunk,
kvContainer, blockID);
ContainerProtos.ChunkInfo chunkInfoProto = writeChunk.getChunkData();
ChunkInfo chunkInfo = ChunkInfo.getFromProtoBuf(chunkInfoProto);
@@ -1137,8 +1142,6 @@ ContainerCommandResponseProto handleWriteChunk(
if (isCommit && writeChunk.hasBlock()) {
long startTime = Time.monotonicNowNanos();
metrics.incContainerOpsMetrics(Type.PutBlock);
- BlockData blockData = BlockData.getFromProtoBuf(
- writeChunk.getBlock().getBlockData());
// optimization for hsync when WriteChunk is in commit phase:
//
// block metadata is piggybacked in the same message.
@@ -1175,6 +1178,24 @@ ContainerCommandResponseProto handleWriteChunk(
return getWriteChunkResponseSuccess(request, blockDataProto);
}
+ private static BlockData validateWriteChunkBlockData(WriteChunkRequestProto
writeChunk,
+ KeyValueContainer kvContainer, BlockID blockID) throws IOException {
+ if (!writeChunk.hasBlock()) {
+ return null;
+ }
+ final BlockData blockData;
+ try {
+ blockData =
BlockData.getFromProtoBuf(writeChunk.getBlock().getBlockData());
+ } catch (IllegalArgumentException ex) {
+ throw new StorageContainerException(ex.getMessage(), ex,
INVALID_ARGUMENT);
+ }
+ BlockUtils.verifyStorageType(kvContainer.getContainerData(),
blockData.getBlockID());
+ if
(!blockID.getContainerBlockID().equals(blockData.getBlockID().getContainerBlockID()))
{
+ throw new StorageContainerException("WriteChunk and PutBlock have
different block IDs", INVALID_ARGUMENT);
+ }
+ return blockData;
+ }
+
private void updateRecoveringContainerTimeout(KeyValueContainer kvContainer)
{
if (kvContainer.getContainerState() != RECOVERING) {
return;
@@ -1272,6 +1293,7 @@ ContainerCommandResponseProto handlePutSmallFile(
BlockData blockData = BlockData.getFromProtoBuf(
putSmallFileReq.getBlock().getBlockData());
Objects.requireNonNull(blockData, "blockData == null");
+ BlockUtils.verifyStorageType(kvContainer.getContainerData(),
blockData.getBlockID());
ContainerProtos.ChunkInfo chunkInfoProto =
putSmallFileReq.getChunkInfo();
ChunkInfo chunkInfo = ChunkInfo.getFromProtoBuf(chunkInfoProto);
diff --git
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/keyvalue/helpers/BlockUtils.java
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/keyvalue/helpers/BlockUtils.java
index a05f44c1a77..0863a26e2fb 100644
---
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/keyvalue/helpers/BlockUtils.java
+++
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/keyvalue/helpers/BlockUtils.java
@@ -20,6 +20,7 @@
import static
org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos.Result.CONTAINER_NOT_FOUND;
import static
org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos.Result.EXPORT_CONTAINER_METADATA_FAILED;
import static
org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos.Result.IMPORT_CONTAINER_METADATA_FAILED;
+import static
org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos.Result.INVALID_ARGUMENT;
import static
org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos.Result.NO_SUCH_BLOCK;
import static
org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos.Result.UNABLE_TO_READ_METADATA_DB;
import static
org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos.Result.UNKNOWN_BCSID;
@@ -37,6 +38,7 @@
import
org.apache.hadoop.hdds.scm.container.common.helpers.StorageContainerException;
import org.apache.hadoop.ozone.OzoneConsts;
import org.apache.hadoop.ozone.container.common.helpers.BlockData;
+import org.apache.hadoop.ozone.container.common.impl.ContainerData;
import org.apache.hadoop.ozone.container.common.interfaces.Container;
import org.apache.hadoop.ozone.container.common.interfaces.DBHandle;
import org.apache.hadoop.ozone.container.common.utils.ContainerCache;
@@ -211,6 +213,23 @@ public static BlockData getBlockData(byte[] bytes) throws
IOException {
}
}
+ /**
+ * Verify if request block storage type matches the container storage type.
+ *
+ * @param containerData container data
+ * @param blockID requested block info
+ * @throws StorageContainerException if storage types do not match
+ */
+ public static void verifyStorageType(ContainerData containerData, BlockID
blockID)
+ throws StorageContainerException {
+ if (blockID.getStorageType() != null && containerData.getStorageType() !=
null &&
+ blockID.getStorageType() != containerData.getStorageType()) {
+ throw new StorageContainerException(String.format(
+ "Block storage type %s does not match container storage type %s",
+ blockID.getStorageType(), containerData.getStorageType()),
INVALID_ARGUMENT);
+ }
+ }
+
/**
* Verify if request block BCSID is supported.
*
diff --git
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/keyvalue/impl/BlockManagerImpl.java
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/keyvalue/impl/BlockManagerImpl.java
index 6e69ed0480f..4d7bd461ab4 100644
---
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/keyvalue/impl/BlockManagerImpl.java
+++
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/keyvalue/impl/BlockManagerImpl.java
@@ -187,6 +187,7 @@ public long persistPutBlock(KeyValueContainer container,
Objects.requireNonNull(data, "data == null");
Preconditions.checkState(data.getContainerID() >= 0, "Container Id " +
"cannot be negative");
+ BlockUtils.verifyStorageType(container.getContainerData(),
data.getBlockID());
KeyValueContainerData containerData = container.getContainerData();
diff --git
a/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/common/helpers/TestBlockData.java
b/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/common/helpers/TestBlockData.java
index 7b6813a6826..208999d3232 100644
---
a/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/common/helpers/TestBlockData.java
+++
b/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/common/helpers/TestBlockData.java
@@ -131,7 +131,7 @@ public void testToString() {
final BlockID blockID = new BlockID(5, 123);
blockID.setBlockCommitSequenceId(42);
final BlockData subject = new BlockData(blockID);
- assertEquals("[blockId=conID: 5 locID: 123 bcsId: 42 replicaIndex: null,
size=0]",
+ assertEquals("[blockId=conID: 5 locID: 123 bcsId: 42 replicaIndex: null
storageType: null, size=0]",
subject.toString());
}
}
diff --git
a/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/common/impl/TestContainerPersistence.java
b/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/common/impl/TestContainerPersistence.java
index c2c2f1b7e89..bd810fb4724 100644
---
a/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/common/impl/TestContainerPersistence.java
+++
b/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/common/impl/TestContainerPersistence.java
@@ -57,6 +57,7 @@
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos;
import
org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos.ChecksumType;
+import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos.Result;
import
org.apache.hadoop.hdds.protocol.proto.StorageContainerDatanodeProtocolProtos;
import org.apache.hadoop.hdds.scm.ScmConfigKeys;
import
org.apache.hadoop.hdds.scm.container.common.helpers.StorageContainerException;
@@ -137,10 +138,17 @@ private void
initSchemaAndVersionInfo(ContainerTestVersionInfo versionInfo) {
}
@BeforeAll
- public static void init() {
+ public static void init() throws IOException {
conf = new OzoneConfiguration();
hddsPath = hddsFile.getPath();
- conf.set(ScmConfigKeys.HDDS_DATANODE_DIR_KEY, hddsPath);
+ StringBuilder hddsDirs = new StringBuilder();
+ for (StorageType storageType : StorageType.values()) {
+ hddsDirs.append('[').append(storageType.name()).append(']');
+ String volume = Files.createTempDirectory(
+ TestContainerPersistence.class.getSimpleName() +
storageType.name()).toFile().getAbsolutePath();
+ hddsDirs.append(volume).append(',');
+ }
+ conf.set(ScmConfigKeys.HDDS_DATANODE_DIR_KEY, hddsDirs.toString());
conf.set(OzoneConfigKeys.OZONE_METADATA_DIRS, hddsPath);
volumeChoosingPolicy = new RoundRobinVolumeChoosingPolicy();
}
@@ -194,9 +202,12 @@ private long getTestContainerID() {
private KeyValueContainer addContainer(ContainerSet cSet, long cID)
throws IOException {
- long commitBytesBefore = 0;
- long commitBytesAfter = 0;
- long commitIncrement = 0;
+ return addContainer(cSet, cID, StorageType.DISK);
+ }
+
+ private KeyValueContainer addContainer(ContainerSet cSet, long cID,
+ StorageType storageType)
+ throws IOException {
KeyValueContainerData data = new KeyValueContainerData(cID,
layout,
ContainerTestHelper.CONTAINER_MAX_SIZE, UUID.randomUUID().toString(),
@@ -204,17 +215,19 @@ private KeyValueContainer addContainer(ContainerSet cSet,
long cID)
data.addMetadata("VOLUME", "shire");
data.addMetadata("owner)", "bilbo");
KeyValueContainer container = new KeyValueContainer(data, conf);
- commitBytesBefore =
StorageVolumeUtil.getHddsVolumesList(volumeSet.getVolumesList()).get(0).getCommittedBytes();
+ Map<HddsVolume, Long> committedBytes = new HashMap<>();
+ for (HddsVolume volume : StorageVolumeUtil.getHddsVolumesList(
+ volumeSet.getVolumesList())) {
+ committedBytes.put(volume, volume.getCommittedBytes());
+ }
- container.create(volumeSet, volumeChoosingPolicy, SCM_ID,
StorageType.DISK);
+ container.create(volumeSet, volumeChoosingPolicy, SCM_ID, storageType);
cSet.addContainer(container);
- commitBytesAfter = container.getContainerData()
- .getVolume().getCommittedBytes();
- commitIncrement = commitBytesAfter - commitBytesBefore;
// did we commit space for the new container?
- assertEquals(commitIncrement,
- ContainerTestHelper.CONTAINER_MAX_SIZE);
+ HddsVolume volume = container.getContainerData().getVolume();
+ assertEquals(ContainerTestHelper.CONTAINER_MAX_SIZE,
+ volume.getCommittedBytes() - committedBytes.get(volume));
return container;
}
@@ -423,7 +436,7 @@ public void testDeleteContainerWithRenaming(
// Set up container 1.
long testContainerID1 = getTestContainerID();
Container<KeyValueContainerData> container1 =
- addContainer(containerSet, testContainerID1);
+ addContainer(containerSet, testContainerID1, StorageType.DISK);
BlockID container1Block = addBlockToContainer(container1);
container1.close();
KeyValueContainerData container1Data = container1.getContainerData();
@@ -432,7 +445,7 @@ public void testDeleteContainerWithRenaming(
// Set up container 2.
long testContainerID2 = getTestContainerID();
Container<KeyValueContainerData> container2 =
- addContainer(containerSet, testContainerID2);
+ addContainer(containerSet, testContainerID2, StorageType.DISK);
BlockID container2Block = addBlockToContainer(container2);
container2.close();
KeyValueContainerData container2Data = container2.getContainerData();
@@ -909,6 +922,66 @@ public void testPutBlock(ContainerTestVersionInfo
versionInfo)
assertEquals(info.getChecksumData(), readChunk.getChecksumData());
}
+ /**
+ * Tests a put block and read block with StorageType.
+ * @throws IOException
+ */
+ @ContainerTestVersionInfo.ContainerTest
+ public void testPutBlockWithStorageType(ContainerTestVersionInfo versionInfo)
+ throws IOException {
+ initSchemaAndVersionInfo(versionInfo);
+ for (StorageType storageType : StorageType.values()) {
+ long testContainerID = getTestContainerID();
+ Container container = addContainer(containerSet, testContainerID,
storageType);
+
+ BlockID blockID = ContainerTestHelper.getTestBlockID(testContainerID, 0,
storageType);
+ ChunkInfo info = writeChunkHelper(blockID);
+ BlockData blockData = new BlockData(blockID);
+ List<ContainerProtos.ChunkInfo> chunkList = new LinkedList<>();
+ chunkList.add(info.getProtoBufMessage());
+ blockData.setChunks(chunkList);
+ blockManager.putBlock(container, blockData);
+ BlockData readBlockData = blockManager.getBlock(container,
blockData.getBlockID());
+ ChunkInfo readChunk =
ChunkInfo.getFromProtoBuf(readBlockData.getChunks().get(0));
+ assertEquals(info.getChecksumData(), readChunk.getChecksumData());
+ assertEquals(storageType, readBlockData.getBlockID().getStorageType());
+ }
+ }
+
+ /**
+ * Tests a put block and read block with StorageType.
+ * @throws IOException
+ */
+ @ContainerTestVersionInfo.ContainerTest
+ public void testInvalidPutBlockWithStorageType(
+ ContainerTestVersionInfo versionInfo) throws IOException {
+ initSchemaAndVersionInfo(versionInfo);
+ // If StorageType in the request is not null,
+ // the PutBlock must put to a Container of the same StorageType.
+ long testContainerID1 = getTestContainerID();
+ Container container1 = addContainer(containerSet, testContainerID1,
+ StorageType.DISK);
+ BlockData blockData1 = getBlockData(testContainerID1, StorageType.DISK);
+ blockManager.putBlock(container1, blockData1);
+
+ long testContainerID2 = getTestContainerID();
+ Container container2 = addContainer(containerSet, testContainerID2,
StorageType.DISK);
+ BlockData blockData2 = getBlockData(testContainerID2, StorageType.SSD);
+ StorageContainerException storageContainerException =
+ assertThrows(StorageContainerException.class, () ->
blockManager.putBlock(container2, blockData2));
+ assertEquals(Result.INVALID_ARGUMENT,
storageContainerException.getResult());
+ }
+
+ private BlockData getBlockData(long containerID, StorageType storageType)
throws IOException {
+ BlockID blockID = ContainerTestHelper.getTestBlockID(containerID, 0,
storageType);
+ ChunkInfo info = writeChunkHelper(blockID);
+ BlockData blockData = new BlockData(blockID);
+ List<ContainerProtos.ChunkInfo> chunkList = new LinkedList<>();
+ chunkList.add(info.getProtoBufMessage());
+ blockData.setChunks(chunkList);
+ return blockData;
+ }
+
/**
* Tests a put block and read block with invalid bcsId.
*
diff --git
a/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/common/impl/TestHddsDispatcher.java
b/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/common/impl/TestHddsDispatcher.java
index 3d1bb301afd..073844f5268 100644
---
a/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/common/impl/TestHddsDispatcher.java
+++
b/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/common/impl/TestHddsDispatcher.java
@@ -58,6 +58,7 @@
import org.apache.hadoop.conf.StorageUnit;
import org.apache.hadoop.fs.StorageType;
import org.apache.hadoop.hdds.client.BlockID;
+import org.apache.hadoop.hdds.client.StorageTypeUtils;
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
import org.apache.hadoop.hdds.fs.MockSpaceUsageCheckFactory;
import org.apache.hadoop.hdds.fs.MockSpaceUsageSource;
@@ -549,9 +550,153 @@ public void
testCreateContainerRejectsInvalidStorageType() throws IOException {
.setStorageTypeID(999)))
.build();
+ ContainerCommandResponseProto response =
+ dispatcher.createContainer(requestWithInvalidStorageType);
+ assertEquals(ContainerProtos.Result.INVALID_ARGUMENT,
response.getResult());
+
+ StorageContainerException exception =
assertThrows(StorageContainerException.class,
+ () ->
dispatcher.validateContainerCommand(requestWithInvalidStorageType));
+ assertEquals(ContainerProtos.Result.INVALID_ARGUMENT,
exception.getResult());
+
+ for (ContainerProtos.Type type :
Arrays.asList(ContainerProtos.Type.StreamInit,
+ ContainerProtos.Type.StreamInitWithPutBlock)) {
+ ContainerCommandRequestProto streamRequest =
requestWithInvalidStorageType.toBuilder()
+ .setCmdType(type).build();
+ assertEquals(ContainerProtos.Result.INVALID_ARGUMENT,
dispatcher.dispatch(streamRequest, null).getResult());
+ StorageContainerException streamException =
assertThrows(StorageContainerException.class,
+ () -> dispatcher.validateContainerCommand(streamRequest));
+ assertEquals(ContainerProtos.Result.INVALID_ARGUMENT,
streamException.getResult());
+ }
+ }
+
+ @Test
+ public void testInvalidStorageTypeDoesNotMarkContainerUnhealthy() throws
IOException {
+ File diskVolume = Files.createTempDirectory(tempDir, "disk").toFile();
+ OzoneConfiguration conf = new OzoneConfiguration();
+ conf.set(ScmConfigKeys.HDDS_DATANODE_DIR_KEY,
diskVolume.getAbsolutePath());
+ DatanodeDetails dd = randomDatanodeDetails();
+ HddsDispatcher dispatcher = createDispatcher(dd, UUID.randomUUID(), conf);
+
+ ContainerCommandRequestProto validWriteChunkRequest =
+ getWriteChunkRequest(dd.getUuidString(), 1L, 1L, null);
+ assertEquals(ContainerProtos.Result.SUCCESS,
+ dispatcher.dispatch(validWriteChunkRequest, null).getResult());
+
+ ContainerCommandRequestProto writeChunkRequest =
+ getWriteChunkRequest(dd.getUuidString(), 1L, 2L, null);
+ WriteChunkRequestProto writeChunk = writeChunkRequest.getWriteChunk();
+ ContainerCommandRequestProto requestWithInvalidStorageType =
+ writeChunkRequest.toBuilder()
+ .setWriteChunk(writeChunk.toBuilder()
+ .setBlockID(writeChunk.getBlockID().toBuilder()
+ .setStorageTypeID(999)))
+ .build();
+
ContainerCommandResponseProto response =
dispatcher.dispatch(requestWithInvalidStorageType, null);
assertEquals(ContainerProtos.Result.INVALID_ARGUMENT,
response.getResult());
+ assertEquals(ContainerProtos.ContainerDataProto.State.OPEN,
+ dispatcher.getContainer(1L).getContainerData().getState());
+
+ for (ContainerProtos.Type type :
Arrays.asList(ContainerProtos.Type.StreamInit,
+ ContainerProtos.Type.StreamInitWithPutBlock)) {
+ ContainerCommandRequestProto streamRequest =
requestWithInvalidStorageType.toBuilder().setCmdType(type).build();
+ assertEquals(ContainerProtos.Result.INVALID_ARGUMENT,
dispatcher.dispatch(streamRequest, null).getResult());
+ assertEquals(ContainerProtos.ContainerDataProto.State.OPEN,
+ dispatcher.getContainer(1L).getContainerData().getState());
+ }
+ }
+
+ @Test
+ public void testStorageTypeMismatchDoesNotMarkContainerUnhealthy() throws
IOException {
+ File diskVolume = Files.createTempDirectory(tempDir, "disk").toFile();
+ OzoneConfiguration conf = new OzoneConfiguration();
+ conf.set(ScmConfigKeys.HDDS_DATANODE_DIR_KEY,
diskVolume.getAbsolutePath());
+ DatanodeDetails dd = randomDatanodeDetails();
+ HddsDispatcher dispatcher = createDispatcher(dd, UUID.randomUUID(), conf);
+
+ ContainerCommandRequestProto validWriteChunkRequest =
+ getWriteChunkRequest(dd.getUuidString(), 1L, 1L, null);
+ assertEquals(ContainerProtos.Result.SUCCESS,
+ dispatcher.dispatch(validWriteChunkRequest, null).getResult());
+
+ ContainerCommandResponseProto putBlockResponse = dispatcher.dispatch(
+ newPutBlock(1L, 2L, HddsProtos.StorageTypeProto.SSD), null);
+ assertEquals(ContainerProtos.Result.INVALID_ARGUMENT,
putBlockResponse.getResult());
+ assertEquals(ContainerProtos.ContainerDataProto.State.OPEN,
+ dispatcher.getContainer(1L).getContainerData().getState());
+
+ ContainerCommandResponseProto putSmallFileResponse = dispatcher.dispatch(
+ newPutSmallFile(1L, 3L, HddsProtos.StorageTypeProto.SSD), null);
+ assertEquals(ContainerProtos.Result.INVALID_ARGUMENT,
putSmallFileResponse.getResult());
+ assertEquals(ContainerProtos.ContainerDataProto.State.OPEN,
+ dispatcher.getContainer(1L).getContainerData().getState());
+
+ ContainerCommandResponseProto writeChunkResponse = dispatcher.dispatch(
+ getWriteChunkRequest(dd.getUuidString(), 1L, 4L,
HddsProtos.StorageTypeProto.SSD), null);
+ assertEquals(ContainerProtos.Result.INVALID_ARGUMENT,
writeChunkResponse.getResult());
+ assertEquals(ContainerProtos.ContainerDataProto.State.OPEN,
+ dispatcher.getContainer(1L).getContainerData().getState());
+
+ for (ContainerProtos.Type type :
Arrays.asList(ContainerProtos.Type.StreamInit,
+ ContainerProtos.Type.StreamInitWithPutBlock)) {
+ ContainerCommandRequestProto streamRequest = getWriteChunkRequest(
+ dd.getUuidString(), 1L, 5L,
HddsProtos.StorageTypeProto.SSD).toBuilder().setCmdType(type).build();
+ StorageContainerException exception =
assertThrows(StorageContainerException.class,
+ () -> dispatcher.getStreamDataChannel(streamRequest, null));
+ assertEquals(ContainerProtos.Result.INVALID_ARGUMENT,
exception.getResult());
+ assertEquals(ContainerProtos.ContainerDataProto.State.OPEN,
+ dispatcher.getContainer(1L).getContainerData().getState());
+ }
+ }
+
+ @Test
+ public void testWriteChunkWithInvalidEmbeddedBlockHasNoSideEffects() throws
IOException {
+ File diskVolume = Files.createTempDirectory(tempDir, "disk").toFile();
+ OzoneConfiguration conf = new OzoneConfiguration();
+ conf.set(ScmConfigKeys.HDDS_DATANODE_DIR_KEY,
diskVolume.getAbsolutePath());
+ DatanodeDetails dd = randomDatanodeDetails();
+ HddsDispatcher dispatcher = createDispatcher(dd, UUID.randomUUID(), conf);
+
+ ContainerCommandRequestProto createRequest =
getWriteChunkRequest(dd.getUuidString(), 1L, 1L, null);
+ assertEquals(ContainerProtos.Result.SUCCESS,
dispatcher.dispatch(createRequest, null).getResult());
+
+ ContainerCommandRequestProto writeRequest =
getWriteChunkRequest(dd.getUuidString(), 1L, 2L,
+ HddsProtos.StorageTypeProto.DISK);
+ ContainerProtos.DatanodeBlockID outerID =
writeRequest.getWriteChunk().getBlockID();
+ ContainerProtos.PutBlockRequestProto putBlock =
+ ContainerTestHelper.getPutBlockRequest(writeRequest).getPutBlock();
+ ContainerProtos.DatanodeBlockID[] embeddedIDs = {
+
outerID.toBuilder().setStorageTypeID(HddsProtos.StorageTypeProto.SSD.getNumber()).build(),
+ outerID.toBuilder().setStorageTypeID(999).build(),
+ outerID.toBuilder().setLocalID(3L).build(),
+ outerID
+ };
+ ContainerProtos.DatanodeBlockID[] outerIDs = {
+ outerID, outerID, outerID,
+
outerID.toBuilder().setStorageTypeID(HddsProtos.StorageTypeProto.SSD.getNumber()).build()
+ };
+
+ for (int i = 0; i < embeddedIDs.length; i++) {
+ ContainerProtos.BlockData embeddedBlock =
+
putBlock.getBlockData().toBuilder().setBlockID(embeddedIDs[i]).build();
+ ContainerCommandRequestProto request = writeRequest.toBuilder()
+ .setWriteChunk(writeRequest.getWriteChunk().toBuilder()
+ .setBlockID(outerIDs[i])
+
.setBlock(putBlock.toBuilder().setBlockData(embeddedBlock).setEof(true)))
+ .build();
+
+ assertEquals(ContainerProtos.Result.INVALID_ARGUMENT,
+ dispatcher.dispatch(request, null).getResult());
+ assertEquals(ContainerProtos.Result.UNABLE_TO_FIND_CHUNK,
+ dispatcher.dispatch(getReadChunkRequest(writeRequest),
null).getResult());
+ ContainerCommandResponseProto listBlocks = dispatcher.dispatch(
+ ContainerTestHelper.getListBlockRequest(writeRequest), null);
+ assertEquals(ContainerProtos.Result.SUCCESS, listBlocks.getResult());
+ assertEquals(0, listBlocks.getListBlock().getBlockDataCount());
+ assertEquals(ContainerProtos.ContainerDataProto.State.OPEN,
+ dispatcher.getContainer(1L).getContainerData().getState());
+ }
}
private void assertContainerDoNotExist(HddsDispatcher hddsDispatcher,
@@ -870,7 +1015,7 @@ private ContainerCommandRequestProto getWriteChunkRequest(
new BlockID(containerId, localId).getDatanodeBlockIDProtobufBuilder();
// TODO: Pass the real storage type from the write path once that BlockID
support StorageType
if (storageType != null) {
- blockID.setStorageTypeID(storageType.getNumber());
+
blockID.setStorageTypeID(StorageTypeUtils.getIDFromProtobuf(storageType));
}
WriteChunkRequestProto.Builder writeChunkRequest = WriteChunkRequestProto
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]