Copilot commented on code in PR #10764:
URL: https://github.com/apache/ozone/pull/10764#discussion_r3584640445


##########
hadoop-hdds/client/src/main/java/org/apache/hadoop/hdds/scm/storage/StreamBlockInputStream.java:
##########
@@ -532,10 +534,14 @@ public void 
onNext(ContainerProtos.ContainerCommandResponseProto containerComman
         ByteBuffer data = readBlock.getData().asReadOnlyByteBuffer();
         if (verifyChecksum) {
           ChecksumData checksumData = 
ChecksumData.getFromProtoBuf(readBlock.getChecksumData());
-          Checksum.verifyChecksum(data, checksumData, 0);
+          if (readBlock.hasChunkInfoList()) {
+            verifyChecksumForReadBlock(data, checksumData, readBlock);
+          } else {
+            throw new IOException("Checksum data is missing for block " + 
getBlockID());
+          }

Review Comment:
   When checksum verification is enabled, the code currently throws if 
chunkInfoList is missing, even if the checksum type is NONE (where verification 
is a no-op). Also, the exception message says "Checksum data is missing" but 
the missing field is chunkInfoList.
   
   This can cause reads to fail unnecessarily (eg. checksum type NONE or older 
servers that don't send chunkInfoList). Consider only requiring chunkInfoList 
when checksum type != NONE, and update the message accordingly.



##########
hadoop-hdds/client/src/main/java/org/apache/hadoop/hdds/scm/storage/StreamBlockInputStream.java:
##########
@@ -628,5 +634,46 @@ public void setStreamingReadResponse(StreamingReadResponse 
streamingReadResponse
     public String toString() {
       return name;
     }
+
+    private void verifyChecksumForReadBlock(
+        ByteBuffer data, ChecksumData checksumData, ReadBlockResponseProto 
readBlock)
+        throws OzoneChecksumException {
+      if (!checksumData.getChecksumType().equals(ChecksumType.NONE)) {
+        int bytesPerChecksum = checksumData.getBytesPerChecksum();
+        long blockOffset = readBlock.getOffset();
+        long readLength = data.remaining();
+        long currentChunkOffset = 0;
+        int checksumIndex = 0;
+        int dataOffset = 0;
+
+        for (ContainerProtos.ChunkInfo chunk : 
readBlock.getChunkInfoList().getChunksList()) {
+          long chunkStart = currentChunkOffset;
+          long chunkEnd = chunkStart + chunk.getLen();
+

Review Comment:
   verifyChecksumForReadBlock derives chunk boundaries from a cumulative 
counter (currentChunkOffset) rather than using the ChunkInfo.offset values 
provided by the server. If chunk offsets are not strictly contiguous / ordered, 
this can compute the wrong overlap ranges and advance checksumIndex incorrectly.
   
   Use chunk.getOffset() for chunkStart (and derive chunkEnd from it) instead 
of assuming contiguous offsets.



##########
hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/client/rpc/read/TestStreamRead.java:
##########
@@ -301,4 +301,44 @@ static String runTestReadKey(String name, SizeInBytes 
keySize, SizeInBytes buffe
     print(name, keySizeByte, elapsedNanos, bufferSize, computedMD5);
     return computedMD5;
   }
+
+  @Test
+  void testSmallChunksWithLargeChecksum() throws Exception {
+    final SizeInBytes bytesPerChecksum = SizeInBytes.valueOf(512);
+    System.out.println("cluster starting ...");
+    try (MiniOzoneCluster cluster = newCluster(bytesPerChecksum.getSizeInt())) 
{
+      cluster.waitForClusterToBeReady();
+      System.out.println("cluster ready");
+      
+      OzoneConfiguration conf = cluster.getConf();
+      OzoneClientConfig clientConfig = conf.getObject(OzoneClientConfig.class);
+      clientConfig.setStreamReadBlock(true);
+      clientConfig.setStreamBufferFlushDelay(false);
+      final OzoneConfiguration steamReadConf = new OzoneConfiguration(conf);
+      steamReadConf.setFromObject(clientConfig);
+
+      try (OzoneClient streamReadClient = 
OzoneClientFactory.getRpcClient(steamReadConf)) {

Review Comment:
   Typo in variable name: `steamReadConf` should be `streamReadConf` for 
clarity (and to match the surrounding `streamReadClient` naming).



##########
hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/scm/protocolPB/ContainerCommandResponseBuilders.java:
##########
@@ -336,14 +338,17 @@ public static ContainerCommandResponseProto 
getReadChunkResponse(
   }
 
   public static ContainerCommandResponseProto getReadBlockResponse(
-      ContainerCommandRequestProto request, ChecksumData checksumData, 
ByteBuffer data, long offset) {
+      ContainerCommandRequestProto request, ChecksumData checksumData,
+      ByteBuffer data, long offset, List<ChunkInfo> chunkInfoList, boolean 
verifyChecksum) {
 
-    ContainerProtos.ReadBlockResponseProto response = 
ContainerProtos.ReadBlockResponseProto.newBuilder()
+    ContainerProtos.ReadBlockResponseProto response = 
ReadBlockResponseProto.newBuilder()
         .setChecksumData(checksumData.getProtoBufMessage())
         .setData(ByteString.copyFrom(data))
         .setOffset(offset)
+        
.setChunkInfoList(ChunkInfoList.newBuilder().addAllChunks(chunkInfoList).build())
         .build();

Review Comment:
   getReadBlockResponse currently always serializes and sends the full 
chunkInfoList, and the verifyChecksum parameter is unused. Sending chunk 
metadata on every ReadBlock response can significantly increase response sizes 
(especially since readBlockImpl can stream many responses per block).
   
   Use verifyChecksum to only attach chunkInfoList when checksum verification 
is enabled (and avoid setting it at all when not needed).



##########
hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/keyvalue/KeyValueHandler.java:
##########
@@ -2225,30 +2225,41 @@ private long readBlockImpl(ContainerCommandRequestProto 
request, RandomAccessFil
   static List<ByteString> getChecksums(long blockOffset, int readLength, int 
bytesPerChunk, int bytesPerChecksum,
       final List<ContainerProtos.ChunkInfo> chunks) {
     assertSame(0, blockOffset % bytesPerChecksum, "blockOffset % 
bytesPerChecksum");
-    final int numChecksums = 1 + (readLength - 1) / bytesPerChecksum;
-    final List<ByteString> checksums = new ArrayList<>(numChecksums);
-    for (int i = 0; i < numChecksums; i++) {
-      // As the checksums are stored "chunk by chunk", we need to figure out 
which chunk we start reading from,
-      // and its offset to pull out the correct checksum bytes for each read.
-      final int n = i * bytesPerChecksum;
-      final long offset = blockOffset + n;
-      final int c = Math.toIntExact(offset / bytesPerChunk);
-      final int chunkOffset = Math.toIntExact(offset % bytesPerChunk);
-      final int csi = chunkOffset / bytesPerChecksum;
-
-      assertTrue(c < chunks.size(),
-          () -> "chunkIndex = " + c + " >= chunk.size()" + chunks.size());
-      final ContainerProtos.ChunkInfo chunk = chunks.get(c);
-      if (c < chunks.size() - 1) {
-        assertSame(bytesPerChunk, chunk.getLen(), "bytesPerChunk");
-      }
-      final ContainerProtos.ChecksumData checksumDataProto = 
chunks.get(c).getChecksumData();
-      assertSame(bytesPerChecksum, checksumDataProto.getBytesPerChecksum(), 
"bytesPerChecksum");
-      final List<ByteString> checksumsList = 
checksumDataProto.getChecksumsList();
-      assertTrue(csi < checksumsList.size(),
-          () -> "checksumIndex = " + csi + " >= checksumsList.size()" + 
checksumsList.size());
-      checksums.add(checksumsList.get(csi));
+    final List<ByteString> checksums = new ArrayList<>();
+
+    long currentChunkOffset = 0;
+    for (ContainerProtos.ChunkInfo chunk : chunks) {
+      long chunkStart = currentChunkOffset;
+      long chunkEnd = chunkStart + chunk.getLen();
+
+      long overlapStart = Math.max(blockOffset, chunkStart);
+      long overlapEnd = Math.min(blockOffset + readLength, chunkEnd);
+

Review Comment:
   getChecksums computes chunkStart/chunkEnd using a cumulative counter 
(currentChunkOffset) instead of the ChunkInfo.offset values. Since ChunkInfo 
already carries the exact offset, relying on a running total can return 
incorrect overlaps if the chunk list is ever non-contiguous or not strictly 
ordered.
   
   Prefer using chunk.getOffset() to determine chunkStart.



-- 
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]

Reply via email to