From 6d805da91da0e6ff175d3c7eca9c651b545a45f4 Mon Sep 17 00:00:00 2001 From: peterxcli Date: Sun, 17 May 2026 18:54:07 +0800 Subject: [PATCH 1/2] handle client malformed container write request Signed-off-by: peterxcli --- .../container/common/impl/HddsDispatcher.java | 1 + .../container/keyvalue/KeyValueHandler.java | 6 +++ .../common/impl/TestHddsDispatcher.java | 42 +++++++++++++++++++ 3 files changed, 49 insertions(+) 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 a109c02e5a1b..249e58df94d4 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 @@ -174,6 +174,7 @@ private boolean canIgnoreException(Result result) { case CLOSED_CONTAINER_IO: case DELETE_ON_OPEN_CONTAINER: case UNSUPPORTED_REQUEST:// Blame client for sending unsupported request. + case MALFORMED_REQUEST:// Blame client for sending malformed request. case CONTAINER_MISSING: case CONTAINER_ALREADY_EXISTS: return true; 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 474b74586448..af7242541abd 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 @@ -33,6 +33,7 @@ import static org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos.Result.INVALID_ARGUMENT; import static org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos.Result.INVALID_CONTAINER_STATE; import static org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos.Result.IO_EXCEPTION; +import static org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos.Result.MALFORMED_REQUEST; import static org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos.Result.PUT_SMALL_FILE_ERROR; import static org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos.Result.UNCLOSED_CONTAINER_IO; import static org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos.Result.UNSUPPORTED_REQUEST; @@ -123,6 +124,7 @@ 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.hdds.utils.db.CodecException; import org.apache.hadoop.hdds.utils.io.RandomAccessFileChannel; import org.apache.hadoop.ozone.OzoneConfigKeys; import org.apache.hadoop.ozone.OzoneConsts; @@ -685,6 +687,10 @@ ContainerCommandResponseProto handlePutBlock( metrics.incContainerBytesStats(Type.PutBlock, numBytes); } catch (StorageContainerException ex) { return ContainerUtils.logAndReturnError(LOG, ex, request); + } catch (CodecException ex) { + return ContainerUtils.logAndReturnError(LOG, + new StorageContainerException("Malformed PutBlock request", ex, + MALFORMED_REQUEST), request); } catch (IOException ex) { return ContainerUtils.logAndReturnError(LOG, new StorageContainerException("Put Key failed", ex, IO_EXCEPTION), 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 b5d4ea10647f..0f10eaf1327e 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 @@ -538,6 +538,48 @@ public void testCreateContainerWhenAlreadyExistsDoesNotMarkUnhealthy() throws IO } } + @Test + public void testMalformedPutBlockDoesNotMarkContainerUnhealthy() throws IOException { + String testDirPath = testDir.getPath(); + try { + UUID scmId = UUID.randomUUID(); + OzoneConfiguration conf = new OzoneConfiguration(); + conf.set(HDDS_DATANODE_DIR_KEY, testDirPath); + conf.set(OzoneConfigKeys.OZONE_METADATA_DIRS, testDirPath); + DatanodeDetails dd = randomDatanodeDetails(); + HddsDispatcher hddsDispatcher = createDispatcher(dd, scmId, conf); + + ContainerCommandRequestProto writeChunkRequest = + getWriteChunkRequest(dd.getUuidString(), 1L, 1L); + ContainerCommandResponseProto writeChunkResponse = + hddsDispatcher.dispatch(writeChunkRequest, null); + assertEquals(ContainerProtos.Result.SUCCESS, writeChunkResponse.getResult()); + + ContainerCommandRequestProto putBlockRequest = + ContainerTestHelper.getPutBlockRequest(writeChunkRequest); + ContainerProtos.BlockData malformedBlockData = + putBlockRequest.getPutBlock().getBlockData().toBuilder() + .setSize(putBlockRequest.getPutBlock().getBlockData().getSize() + 1) + .build(); + ContainerCommandRequestProto malformedPutBlockRequest = + putBlockRequest.toBuilder() + .setPutBlock(putBlockRequest.getPutBlock().toBuilder() + .setBlockData(malformedBlockData)) + .build(); + + ContainerCommandResponseProto response = + hddsDispatcher.dispatch(malformedPutBlockRequest, null); + assertEquals(ContainerProtos.Result.MALFORMED_REQUEST, response.getResult()); + + Container container = hddsDispatcher.getContainer(1L); + assertNotNull(container); + assertTrue(container.getContainerData().isOpen()); + assertFalse(container.getContainerData().isUnhealthy()); + } finally { + ContainerMetrics.remove(); + } + } + @Test public void testDuplicateWriteChunkAndPutBlockRequest() throws IOException { String testDirPath = testDir.getPath(); From 1fbd29ed09a469bf7e8e6c6749d4a43c25650203 Mon Sep 17 00:00:00 2001 From: peterxcli Date: Sun, 17 May 2026 22:32:51 +0800 Subject: [PATCH 2/2] fix test Signed-off-by: peterxcli --- .../TestOnDemandContainerScannerIntegration.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/dn/scanner/TestOnDemandContainerScannerIntegration.java b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/dn/scanner/TestOnDemandContainerScannerIntegration.java index e81b3244a965..5f1b049c0286 100644 --- a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/dn/scanner/TestOnDemandContainerScannerIntegration.java +++ b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/dn/scanner/TestOnDemandContainerScannerIntegration.java @@ -35,6 +35,7 @@ import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos; import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos.ContainerDataProto.State; import org.apache.hadoop.ozone.HddsDatanodeService; +import org.apache.hadoop.ozone.common.Checksum; import org.apache.hadoop.ozone.container.common.interfaces.Container; import org.apache.hadoop.ozone.container.common.interfaces.ContainerDispatcher; import org.apache.hadoop.ozone.container.common.utils.ContainerLogger; @@ -205,7 +206,7 @@ void testOnDemandScanTriggeredByUnhealthyContainer() throws Exception { .getOnDemandScanner().getMetrics(); int initialScannedCount = scannerMetrics.getNumContainersScanned(); - // Create a PutBlock request with malformed block data to trigger internal error + // Create a PutBlock request for a chunk that was never written to trigger internal error ContainerProtos.ContainerCommandRequestProto writeFailureRequest = ContainerProtos.ContainerCommandRequestProto.newBuilder() .setCmdType(ContainerProtos.Type.PutBlock) @@ -218,7 +219,13 @@ void testOnDemandScanTriggeredByUnhealthyContainer() throws Exception { .setLocalID(999L) .setBlockCommitSequenceId(1) .build()) - .setSize(1024) // Size mismatch with chunks + .addChunks(ContainerProtos.ChunkInfo.newBuilder() + .setChunkName("missing-chunk") + .setOffset(0) + .setLen(1024) + .setChecksumData(Checksum.getNoChecksumDataProto()) + .build()) + .setSize(1024) .build()) .build()) .build();