This is an automated email from the ASF dual-hosted git repository.

sadanand48 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ozone.git


The following commit(s) were added to refs/heads/master by this push:
     new 2076d7facfc HDDS-15521. StreamBlockInputStream fails with 
TimeoutIOException without retry or failover. (#10479)
2076d7facfc is described below

commit 2076d7facfcd2933969b58c779c44d2f505d6fc9
Author: Sadanand Shenoy <[email protected]>
AuthorDate: Tue Jul 7 14:13:33 2026 +0530

    HDDS-15521. StreamBlockInputStream fails with TimeoutIOException without 
retry or failover. (#10479)
---
 .../apache/hadoop/hdds/scm/XceiverClientGrpc.java  |  14 +
 .../hdds/scm/storage/StreamBlockInputStream.java   |  51 +++-
 .../scm/storage/TestStreamBlockInputStream.java    |  10 +-
 .../hadoop/hdds/utils/ConnectionFailureUtils.java  |  28 ++
 .../rpc/read/TestStreamReadDatanodeFailover.java   | 287 +++++++++++++++++++++
 5 files changed, 375 insertions(+), 15 deletions(-)

diff --git 
a/hadoop-hdds/client/src/main/java/org/apache/hadoop/hdds/scm/XceiverClientGrpc.java
 
b/hadoop-hdds/client/src/main/java/org/apache/hadoop/hdds/scm/XceiverClientGrpc.java
index 90e581e8f4b..8c52f339027 100644
--- 
a/hadoop-hdds/client/src/main/java/org/apache/hadoop/hdds/scm/XceiverClientGrpc.java
+++ 
b/hadoop-hdds/client/src/main/java/org/apache/hadoop/hdds/scm/XceiverClientGrpc.java
@@ -30,6 +30,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Set;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
@@ -603,9 +604,22 @@ public void streamRead(ContainerCommandRequestProto 
request,
 
   @Override
   public void initStreamRead(BlockID blockID, StreamingReaderSpi 
streamObserver) throws IOException {
+    initStreamRead(blockID, streamObserver, Collections.emptySet());
+  }
+
+  /**
+   * Start a streaming read, skipping datanodes that previously failed for 
this block stream.
+   */
+  public void initStreamRead(BlockID blockID, StreamingReaderSpi 
streamObserver,
+      Set<DatanodeID> excludedDatanodes) throws IOException {
     final List<DatanodeDetails> datanodeList = sortDatanodes(null, 
ContainerProtos.Type.ReadBlock);
     IOException lastException = null;
     for (DatanodeDetails dn : datanodeList) {
+      if (excludedDatanodes.contains(dn.getID())) {
+        LOG.debug("Skipping excluded datanode {} (uuid={}) for initStreamRead 
{}",
+            dn, dn.getUuidString(), blockID.getContainerBlockID());
+        continue;
+      }
       try {
         checkOpen(dn);
         semaphore.acquire();
diff --git 
a/hadoop-hdds/client/src/main/java/org/apache/hadoop/hdds/scm/storage/StreamBlockInputStream.java
 
b/hadoop-hdds/client/src/main/java/org/apache/hadoop/hdds/scm/storage/StreamBlockInputStream.java
index dd3928f9137..975c0511832 100644
--- 
a/hadoop-hdds/client/src/main/java/org/apache/hadoop/hdds/scm/storage/StreamBlockInputStream.java
+++ 
b/hadoop-hdds/client/src/main/java/org/apache/hadoop/hdds/scm/storage/StreamBlockInputStream.java
@@ -23,6 +23,8 @@
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.time.Duration;
+import java.util.HashSet;
+import java.util.Set;
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutionException;
@@ -36,6 +38,8 @@
 import org.apache.hadoop.fs.FSExceptionMessages;
 import org.apache.hadoop.hdds.StringUtils;
 import org.apache.hadoop.hdds.client.BlockID;
+import org.apache.hadoop.hdds.protocol.DatanodeDetails;
+import org.apache.hadoop.hdds.protocol.DatanodeID;
 import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos;
 import 
org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos.ReadBlockResponseProto;
 import org.apache.hadoop.hdds.scm.OzoneClientConfig;
@@ -47,6 +51,7 @@
 import 
org.apache.hadoop.hdds.scm.container.common.helpers.StorageContainerException;
 import org.apache.hadoop.hdds.scm.pipeline.Pipeline;
 import org.apache.hadoop.hdds.security.token.OzoneBlockTokenIdentifier;
+import org.apache.hadoop.hdds.utils.ConnectionFailureUtils;
 import org.apache.hadoop.io.retry.RetryPolicy;
 import org.apache.hadoop.ozone.common.Checksum;
 import org.apache.hadoop.ozone.common.ChecksumData;
@@ -91,6 +96,7 @@ public class StreamBlockInputStream extends 
BlockExtendedInputStream {
   private final Function<BlockID, BlockLocationInfo> refreshFunction;
   private final RetryPolicy retryPolicy;
   private int retries = 0;
+  private final Set<DatanodeID> failedStreamingDatanodes = new HashSet<>();
 
   public StreamBlockInputStream(
       BlockID blockID, long length, Pipeline pipeline,
@@ -171,13 +177,19 @@ private synchronized boolean dataAvailableToRead(int 
length, boolean preRead) th
     if (position >= blockLength) {
       return false;
     }
-    initialize();
-
-    if (bufferHasRemaining()) {
-      return true;
+    while (true) {
+      try {
+        initialize();
+        if (bufferHasRemaining()) {
+          return true;
+        }
+        buffer = streamingReader.read(length, preRead);
+        retries = 0;
+        return bufferHasRemaining();
+      } catch (IOException ex) {
+        handleExceptions(ex);
+      }
     }
-    buffer = streamingReader.read(length, preRead);
-    return bufferHasRemaining();
   }
 
   private synchronized void advancePosition(long delta) {
@@ -293,7 +305,7 @@ private synchronized void initialize() throws IOException {
       try {
         acquireClient();
         final StreamingReader reader = new StreamingReader();
-        xceiverClient.initStreamRead(blockID, reader);
+        xceiverClient.initStreamRead(blockID, reader, 
failedStreamingDatanodes);
         streamingReader = reader;
       } catch (IOException ioe) {
         handleExceptions(ioe);
@@ -327,10 +339,14 @@ synchronized void readBlockImpl(long length) throws 
IOException {
   }
 
   private void handleExceptions(IOException cause) throws IOException {
-    if (cause instanceof StorageContainerException || 
isConnectivityIssue(cause)) {
-      if (shouldRetryRead(cause, retryPolicy, retries++)) {
+    IOException root = ConnectionFailureUtils.unwrapCause(cause);
+    if (root instanceof StorageContainerException || isConnectivityIssue(root) 
||
+         root instanceof TimeoutIOException) {
+      if (shouldRetryRead(root, retryPolicy, retries++)) {
+        recordFailedStreamingDatanode();
         releaseClient();
-        refreshBlockInfo(cause);
+        refreshBlockInfo(root);
+        requestedLength = position;
         LOG.warn("Refreshing block data to read block {} due to {}", blockID, 
cause.getMessage());
       } else {
         throw cause;
@@ -340,6 +356,21 @@ private void handleExceptions(IOException cause) throws 
IOException {
     }
   }
 
+  private void recordFailedStreamingDatanode() {
+    if (streamingReader == null) {
+      return;
+    }
+    final StreamingReadResponse response = streamingReader.getResponse();
+    if (response == null) {
+      return;
+    }
+    final DatanodeDetails dn = response.getDatanodeDetails();
+    if (failedStreamingDatanodes.add(dn.getID())) {
+      LOG.warn("Excluding DataNode {} from streaming read retries for block 
{}",
+          dn, blockID);
+    }
+  }
+
   protected synchronized void releaseClient() {
     if (xceiverClientFactory != null && xceiverClient != null) {
       closeStream();
diff --git 
a/hadoop-hdds/client/src/test/java/org/apache/hadoop/hdds/scm/storage/TestStreamBlockInputStream.java
 
b/hadoop-hdds/client/src/test/java/org/apache/hadoop/hdds/scm/storage/TestStreamBlockInputStream.java
index 4cdf13032b4..9e1ff0893ec 100644
--- 
a/hadoop-hdds/client/src/test/java/org/apache/hadoop/hdds/scm/storage/TestStreamBlockInputStream.java
+++ 
b/hadoop-hdds/client/src/test/java/org/apache/hadoop/hdds/scm/storage/TestStreamBlockInputStream.java
@@ -224,7 +224,7 @@ public void 
testPollDoesNotDropQueuedItemWhenFutureCompletesFirst() throws Excep
       reader.setStreamingReadResponse(streamingReadResponse);
       readerRef.set(reader);
       return null;
-    }).when(xceiverClient).initStreamRead(any(BlockID.class), any());
+    }).when(xceiverClient).initStreamRead(any(BlockID.class), any(), any());
 
     // Simulate the race: when the client sends a ReadBlock request, the server
     // responds with data (onNext) and closes the stream (onCompleted) before
@@ -304,7 +304,7 @@ private XceiverClientGrpc mockStreamingReadClient(byte[] 
data,
           .setReadBlock(readBlock)
           .build());
       return null;
-    }).when(xceiverClient).initStreamRead(any(BlockID.class), any());
+    }).when(xceiverClient).initStreamRead(any(BlockID.class), any(), any());
 
     return xceiverClient;
   }
@@ -338,7 +338,7 @@ public void 
testReadDoesNotDropQueuedItemsWhenFutureIsDoneOnSecondCall() throws
       reader.setStreamingReadResponse(streamingReadResponse);
       readerRef.set(reader);
       return null;
-    }).when(xceiverClient).initStreamRead(any(BlockID.class), any());
+    }).when(xceiverClient).initStreamRead(any(BlockID.class), any(), any());
 
     // Server delivers both 4-byte chunks plus onCompleted() in one synchronous
     // call. After streamRead() returns: queue=[chunk1, chunk2], isDone=true.
@@ -388,7 +388,7 @@ public void 
testReadGetsFreshResponseTimeoutAfterStreamReadWait() throws Excepti
       reader.setStreamingReadResponse(streamingReadResponse);
       readerRef.set(reader);
       return null;
-    }).when(xceiverClient).initStreamRead(any(BlockID.class), any());
+    }).when(xceiverClient).initStreamRead(any(BlockID.class), any(), any());
     doAnswer(inv -> {
       Thread.sleep(450);
       Thread responseThread = new Thread(() -> {
@@ -437,7 +437,7 @@ public void 
testReadWithoutNewRequestGetsFreshTimeoutBudget() throws Exception {
       reader.setStreamingReadResponse(streamingReadResponse);
       readerRef.set(reader);
       return null;
-    }).when(xceiverClient).initStreamRead(any(BlockID.class), any());
+    }).when(xceiverClient).initStreamRead(any(BlockID.class), any(), any());
     doAnswer(inv -> {
       streamReads.incrementAndGet();
       readerRef.get().onNext(buildResponseProto(new byte[] {1}, 0));
diff --git 
a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/utils/ConnectionFailureUtils.java
 
b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/utils/ConnectionFailureUtils.java
index 62cd868c763..d7f45b1cd7e 100644
--- 
a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/utils/ConnectionFailureUtils.java
+++ 
b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/utils/ConnectionFailureUtils.java
@@ -18,11 +18,15 @@
 package org.apache.hadoop.hdds.utils;
 
 import java.io.EOFException;
+import java.io.IOException;
 import java.net.ConnectException;
 import java.net.NoRouteToHostException;
 import java.net.SocketException;
 import java.net.SocketTimeoutException;
 import java.net.UnknownHostException;
+import java.util.concurrent.ExecutionException;
+import 
org.apache.hadoop.hdds.scm.container.common.helpers.StorageContainerException;
+import org.apache.ratis.protocol.exceptions.TimeoutIOException;
 
 /**
  * Shared classifier for exceptions where the cached peer IP is no longer
@@ -100,4 +104,28 @@ public static boolean isConnectionFailure(Throwable t) {
     }
     return false;
   }
+
+  /**
+   * Returns the first {@link StorageContainerException} or
+   * {@link TimeoutIOException} in {@code ex}'s cause chain (through
+   * {@link ExecutionException} and nested {@link IOException} wrappers).
+   */
+  public static IOException unwrapCause(IOException ex) {
+    Throwable t = ex;
+    while (t != null) {
+      if (t instanceof TimeoutIOException || t instanceof 
StorageContainerException) {
+        return (IOException) t;
+      }
+      if (t instanceof ExecutionException && t.getCause() != null) {
+        t = t.getCause();
+        continue;
+      }
+      if (t.getCause() instanceof IOException) {
+        t = t.getCause();
+        continue;
+      }
+      break;
+    }
+    return ex;
+  }
 }
diff --git 
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/client/rpc/read/TestStreamReadDatanodeFailover.java
 
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/client/rpc/read/TestStreamReadDatanodeFailover.java
new file mode 100644
index 00000000000..27a971873be
--- /dev/null
+++ 
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/client/rpc/read/TestStreamReadDatanodeFailover.java
@@ -0,0 +1,287 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hadoop.ozone.client.rpc.read;
+
+import static 
org.apache.hadoop.hdds.protocol.proto.HddsProtos.ReplicationFactor.THREE;
+import static 
org.apache.hadoop.ozone.client.OzoneClientTestUtils.assertKeyContent;
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.UUID;
+import org.apache.commons.lang3.RandomUtils;
+import org.apache.hadoop.hdds.client.RatisReplicationConfig;
+import org.apache.hadoop.hdds.conf.OzoneConfiguration;
+import org.apache.hadoop.hdds.protocol.DatanodeDetails;
+import org.apache.hadoop.hdds.scm.OzoneClientConfig;
+import org.apache.hadoop.hdds.scm.ScmConfigKeys;
+import org.apache.hadoop.hdds.scm.StreamingReadResponse;
+import org.apache.hadoop.hdds.scm.container.ContainerID;
+import org.apache.hadoop.hdds.scm.container.ContainerInfo;
+import org.apache.hadoop.hdds.scm.pipeline.Pipeline;
+import org.apache.hadoop.hdds.scm.server.StorageContainerManager;
+import org.apache.hadoop.hdds.scm.storage.StreamBlockInputStream;
+import org.apache.hadoop.hdds.utils.IOUtils;
+import org.apache.hadoop.ozone.MiniOzoneCluster;
+import org.apache.hadoop.ozone.TestDataUtil;
+import org.apache.hadoop.ozone.client.ObjectStore;
+import org.apache.hadoop.ozone.client.OzoneBucket;
+import org.apache.hadoop.ozone.client.OzoneClient;
+import org.apache.hadoop.ozone.client.OzoneClientFactory;
+import org.apache.hadoop.ozone.client.io.KeyInputStream;
+import org.apache.hadoop.ozone.om.TestBucket;
+import org.apache.hadoop.ozone.om.helpers.OmKeyArgs;
+import org.apache.hadoop.ozone.om.helpers.OmKeyLocationInfo;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
+
+/**
+ * Verifies that streaming block reads fail over to a healthy replica when the
+ * datanode serving the stream becomes unavailable, matching legacy
+ * {@code BlockInputStream} behavior.
+ *
+ * <p>With {@code ozone.client.stream.readblock.enable=true}, reads currently
+ * do not fail over correctly when the streaming datanode stops responding.
+ */
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+class TestStreamReadDatanodeFailover {
+
+  private static final Duration STREAM_READ_TIMEOUT = Duration.ofSeconds(3);
+  private static final int PIPELINE_READY_TIMEOUT_MS = 30_000;
+
+  private MiniOzoneCluster cluster;
+  private final Set<DatanodeDetails> stoppedDatanodes = new HashSet<>();
+
+  @BeforeAll
+  void setup() throws Exception {
+    cluster = newCluster();
+    cluster.waitForClusterToBeReady();
+  }
+
+  @BeforeEach
+  void waitForPipeline() throws Exception {
+    cluster.waitForPipelineTobeReady(THREE, PIPELINE_READY_TIMEOUT_MS);
+  }
+
+  @AfterEach
+  void resetDatanodes() throws Exception {
+    if (stoppedDatanodes.isEmpty()) {
+      return;
+    }
+    List<DatanodeDetails> toRestart = new ArrayList<>(stoppedDatanodes);
+    stoppedDatanodes.clear();
+    for (DatanodeDetails dn : toRestart) {
+      cluster.restartHddsDatanode(dn, false);
+    }
+    cluster.waitForClusterToBeReady();
+  }
+
+  @AfterAll
+  void cleanup() {
+    IOUtils.closeQuietly(cluster);
+  }
+
+  /**
+   * Legacy (Async) chunk reads succeed after stopping one pipeline datanode.
+   */
+  @Test
+  void testAsyncReadFailoverWhenDatanodeStopped() throws Exception {
+    try (OzoneClient client = cluster.newClient()) {
+      ObjectStore store = client.getObjectStore();
+      OzoneBucket bucket = createBucket(store);
+      String keyName = newKeyName();
+      byte[] content = RandomUtils.secure().randomBytes(32 * 1024);
+      TestDataUtil.createKey(bucket, keyName,
+          RatisReplicationConfig.getInstance(THREE), content);
+
+      List<DatanodeDetails> datanodes = getPipelineDatanodes(cluster, bucket, 
keyName);
+      assertEquals(3, datanodes.size());
+
+      stopDatanode(datanodes.get(0));
+      assertKeyContent(bucket, keyName, content);
+
+      stopDatanode(datanodes.get(1));
+      assertKeyContent(bucket, keyName, content);
+    }
+  }
+
+  /**
+   * Streaming reads should succeed after stopping pipeline datanodes when at
+   * least one healthy replica remains, same as the legacy path.
+   */
+  @Test
+  void testStreamReadFailoverWhenDatanodesStopped() throws Exception {
+    OzoneConfiguration streamConf = streamReadConfig(cluster.getConf());
+    try (OzoneClient client = OzoneClientFactory.getRpcClient(streamConf)) {
+      ObjectStore store = client.getObjectStore();
+      OzoneBucket bucket = createBucket(store);
+      String keyName = newKeyName();
+      byte[] content = RandomUtils.secure().randomBytes(32 * 1024);
+      TestDataUtil.createKey(bucket, keyName,
+          RatisReplicationConfig.getInstance(THREE), content);
+
+      List<DatanodeDetails> datanodes = getPipelineDatanodes(cluster, bucket, 
keyName);
+      assertEquals(3, datanodes.size());
+
+      // Sanity check: streaming read works with all datanodes up.
+      assertKeyContent(bucket, keyName, content);
+
+      stopDatanode(datanodes.get(0));
+      assertKeyContent(bucket, keyName, content);
+
+      stopDatanode(datanodes.get(1));
+      assertKeyContent(bucket, keyName, content);
+    }
+  }
+
+  /**
+   * After a streaming read has started, stopping the datanode serving the
+   * stream must not break the read. Legacy reads fail over and continue from
+   * the current offset; streaming reads currently fail (timeout or wrong 
data).
+   */
+  @Test
+  void testStreamReadFailoverAfterActiveDatanodeStopped() throws Exception {
+    OzoneConfiguration streamConf = streamReadConfig(cluster.getConf());
+    try (OzoneClient streamClient = 
OzoneClientFactory.getRpcClient(streamConf);
+         OzoneClient legacyClient = cluster.newClient()) {
+      TestBucket streamBucket = TestBucket.newBuilder(streamClient).build();
+      OzoneBucket legacyBucket = legacyClient.getObjectStore()
+          .getVolume(streamBucket.delegate().getVolumeName())
+          .getBucket(streamBucket.delegate().getName());
+
+      String keyName = newKeyName();
+      byte[] content = streamBucket.writeRandomBytes(keyName, 32 * 1024);
+
+      List<DatanodeDetails> datanodes =
+          getPipelineDatanodes(cluster, streamBucket.delegate(), keyName);
+      assertEquals(3, datanodes.size());
+
+      // Legacy control: stopping one pipeline datanode mid-read still 
succeeds.
+      try (InputStream legacyIn = legacyBucket.readKey(keyName)) {
+        assertNotEquals(-1, legacyIn.read());
+        stopDatanode(datanodes.get(0));
+        readRemaining(legacyIn, content, 1);
+      }
+
+      // Streaming read: stop the datanode actively serving the stream.
+      // This fails today without a proper streaming read failover fix.
+      try (KeyInputStream streamIn = streamBucket.getKeyInputStream(keyName)) {
+        assertNotEquals(-1, streamIn.read());
+        StreamBlockInputStream blockStream =
+            (StreamBlockInputStream) streamIn.getPartStreams().get(0);
+        DatanodeDetails activeDatanode = 
getActiveStreamingDatanode(blockStream);
+        assertTrue(datanodes.contains(activeDatanode),
+            "Active streaming datanode should belong to the key pipeline");
+        stopDatanode(activeDatanode);
+        readRemaining(streamIn, content, 1);
+      }
+    }
+  }
+
+  private void stopDatanode(DatanodeDetails dn) throws IOException {
+    cluster.shutdownHddsDatanode(dn);
+    stoppedDatanodes.add(dn);
+  }
+
+  private static void readRemaining(InputStream in, byte[] expected, int 
offset)
+      throws IOException {
+    byte[] actual = org.apache.commons.io.IOUtils.readFully(in, 
expected.length - offset);
+    assertArrayEquals(
+        java.util.Arrays.copyOfRange(expected, offset, expected.length),
+        actual);
+  }
+
+  private static DatanodeDetails 
getActiveStreamingDatanode(StreamBlockInputStream blockStream)
+      throws ReflectiveOperationException {
+    Field streamingReaderField = 
StreamBlockInputStream.class.getDeclaredField("streamingReader");
+    streamingReaderField.setAccessible(true);
+    Object streamingReader = streamingReaderField.get(blockStream);
+    assertTrue(streamingReader != null, "Streaming reader should be 
initialized after first read");
+
+    Method getResponse = 
streamingReader.getClass().getDeclaredMethod("getResponse");
+    getResponse.setAccessible(true);
+    StreamingReadResponse response = (StreamingReadResponse) 
getResponse.invoke(streamingReader);
+    assertTrue(response != null, "Streaming read response should be 
initialized");
+    return response.getDatanodeDetails();
+  }
+
+  private static OzoneConfiguration streamReadConfig(OzoneConfiguration base) {
+    OzoneClientConfig clientConfig = base.getObject(OzoneClientConfig.class);
+    clientConfig.setStreamReadBlock(true);
+    clientConfig.setStreamReadTimeout(STREAM_READ_TIMEOUT);
+    OzoneConfiguration conf = new OzoneConfiguration(base);
+    conf.setFromObject(clientConfig);
+    return conf;
+  }
+
+  private static MiniOzoneCluster newCluster() throws IOException {
+    OzoneConfiguration conf = new OzoneConfiguration();
+    conf.setInt(ScmConfigKeys.OZONE_SCM_PIPELINE_OWNER_CONTAINER_COUNT, 1);
+    conf.setInt(ScmConfigKeys.OZONE_DATANODE_PIPELINE_LIMIT, 1);
+    conf.setInt(ScmConfigKeys.OZONE_SCM_RATIS_PIPELINE_LIMIT, 5);
+    return MiniOzoneCluster.newBuilder(conf)
+        .setNumDatanodes(3)
+        .build();
+  }
+
+  private static OzoneBucket createBucket(ObjectStore store) throws 
IOException {
+    String volumeName = UUID.randomUUID().toString();
+    store.createVolume(volumeName);
+    String bucketName = UUID.randomUUID().toString();
+    store.getVolume(volumeName).createBucket(bucketName);
+    return store.getVolume(volumeName).getBucket(bucketName);
+  }
+
+  private static String newKeyName() {
+    return "key-" + UUID.randomUUID();
+  }
+
+  private static List<DatanodeDetails> getPipelineDatanodes(MiniOzoneCluster 
cluster,
+      OzoneBucket bucket, String keyName) throws IOException {
+    OmKeyArgs keyArgs = new OmKeyArgs.Builder()
+        .setVolumeName(bucket.getVolumeName())
+        .setBucketName(bucket.getName())
+        .setKeyName(keyName)
+        .build();
+    OmKeyLocationInfo keyInfo = cluster.getOzoneManager().lookupKey(keyArgs)
+        .getKeyLocationVersions().get(0)
+        .getBlocksLatestVersionOnly().get(0);
+    long containerID = keyInfo.getContainerID();
+
+    StorageContainerManager scm = cluster.getStorageContainerManager();
+    ContainerInfo container = scm.getContainerManager()
+        .getContainer(ContainerID.valueOf(containerID));
+    Pipeline pipeline = scm.getPipelineManager()
+        .getPipeline(container.getPipelineID());
+    return pipeline.getNodes();
+  }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to