[ 
https://issues.apache.org/jira/browse/HADOOP-19946?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18141841#comment-18141841
 ] 

ASF GitHub Bot commented on HADOOP-19946:
-----------------------------------------

anmolanmol1234 commented on code in PR #8830:
URL: https://github.com/apache/hadoop/pull/8830#discussion_r4522564423


##########
hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/services/ReadBufferManagerV2.java:
##########
@@ -279,16 +276,135 @@ void init() {
   }
 
   /**
-   * {@link AbfsInputStream} calls this method to queueing read-ahead.
-   * @param stream which read-ahead is requested from.
-   * @param requestedOffset The offset in the file which should be read.
-   * @param requestedLength The length to read.
+   * {@inheritDoc}
    */
   @Override
   public void queueReadAhead(final AbfsInputStream stream,
       final long requestedOffset,
       final int requestedLength,
+      final List<BlobLayout.BlobRange> segments,
       TracingContext tracingContext) {
+    synchronized (this) {
+      if (isAlreadyQueued(stream.getETag(), requestedOffset)) {
+        // Already queued for this offset, so skip queuing.
+        printTraceLog(
+            "Skipping queuing readAhead for file: {}, with eTag: {}, "
+                + "offset: {}, triggered by stream: {} as it is already 
queued",
+            stream.getPath(), stream.getETag(), requestedOffset,
+            stream.hashCode());
+        return;
+      }
+
+      if (isFreeListEmpty() && !tryMemoryUpscale() && !tryEvict()) {
+        // No buffers are available and more buffers cannot be created. Skip 
queuing.
+        printTraceLog(
+            "Skipping queuing readAhead for file: {}, with eTag: {}, offset: 
{}, triggered by stream: {} as no buffers are available",
+            stream.getPath(), stream.getETag(), requestedOffset,
+            stream.hashCode());
+        return;
+      }
+
+      // Create new parent for this window
+      ReadBuffer parentBuffer = new ReadBuffer();
+      // To map buffer with stream that requested it
+      parentBuffer.setStream(stream);
+      // To map buffer with file it belongs to
+      parentBuffer.setETag(stream.getETag());
+      parentBuffer.setPath(stream.getPath());
+      parentBuffer.setOffset(requestedOffset);
+      parentBuffer.setBufferOffset(0);
+      parentBuffer.setLength(0);
+      parentBuffer.setStatus(ReadBufferStatus.NOT_AVAILABLE);
+      parentBuffer.setTracingContext(tracingContext);
+      parentBuffer.setRequestedLength(requestedLength);
+      parentBuffer.setParentToTrue();
+
+      if (isFreeListEmpty()) {
+        /*
+         * By now there should be at least one buffer available.
+         * This is to double sure that after upscaling or eviction,
+         * we still have free buffer available. If not, we skip queueing.
+         */
+        return;
+      }
+      Integer bufferIndex = popFromFreeList();
+      if (bufferIndex >= bufferPool.length) {
+        // This should never happen.
+        printTraceLog(
+            "Skipping queuing readAhead for file: {}, with eTag: {}, offset: 
{}, triggered by stream: {} as invalid buffer index popped from free list",
+            stream.getPath(), stream.getETag(), requestedOffset,
+            stream.hashCode());
+        return;
+      }
+      parentBuffer.setBuffer(bufferPool[bufferIndex]);
+      parentBuffer.setBufferindex(bufferIndex);
+
+      printTraceLog(
+          "Created parent buffer for offset: {}, with index: {}, for file: {}, 
eTag: {}",
+          requestedOffset, bufferIndex, stream.getPath(), stream.getETag());
+
+      // Iterate over the segments and create children
+      for (BlobLayout.BlobRange segment : segments) {
+        long readStart = segment.start();
+        long readEnd = segment.end();
+        int rangeLength = Math.toIntExact(readEnd - readStart + 1);
+
+        printTraceLog(
+            "Start Queueing readAhead childBuffer for file: {}, with eTag: {}, 
"
+                + "offset: {}, length: {}, segments: {}, triggered by stream: 
{}",
+            stream.getPath(), stream.getETag(), readStart, rangeLength,
+            segments.size(), stream.hashCode());
+
+        // Create child buffer for this segment
+        ReadBuffer childBuffer = new ReadBuffer();
+        childBuffer.setStream(stream);
+        childBuffer.setETag(
+            stream.getETag()); // To map buffer with file it belongs to
+        childBuffer.setPath(stream.getPath());
+        childBuffer.setOffset(readStart); // File position for this segment
+        childBuffer.setBufferOffset(
+            parentBuffer.getBufferOffset()); // Offset in shared array
+        childBuffer.setLength(0);
+        childBuffer.setRequestedLength(rangeLength);
+        childBuffer.setStatus(ReadBufferStatus.NOT_AVAILABLE);
+        childBuffer.setLatch(new CountDownLatch(1));
+        childBuffer.setTracingContext(tracingContext);
+
+        childBuffer.setBuffer(parentBuffer.getBuffer()); // Share parent's 
array
+        childBuffer.setBufferindex(
+            parentBuffer.getBufferindex()); // Share parent's index
+        childBuffer.setParentBuffer(parentBuffer); // Link to parent
+        childBuffer.setReadEndpoint(segment.host());
+
+        parentBuffer.pushToChildBufferList(childBuffer);
+        parentBuffer.incrementActiveChildren();
+        parentBuffer.setBufferOffset(

Review Comment:
   Child buffers are placed one after another in the shared array, but 
getBlockFromCompletedQueue reads from position \- parentOffset. That only works 
if the ranges are contiguous and the first one starts at requestedOffset.
   
   getBlobRanges can return partial coverage, for example when a layout fetch 
times out or fails, and readInternal only falls back when the list is empty. 
Example: for a window of 0\-4 MB where only [1 MB\-4 MB) is returned, the child 
writes file bytes from 1 MB into array index 0. A read at position 0 then 
silently returns the wrong data. With a gap in the middle, a read can hit 
unfilled or stale pool bytes.
   
   If the service can return overlapping ranges from different hosts 
(BlobLayout merges only same\-host ranges), the summed lengths could also 
overflow the shared buffer.
   
   Can we set each child's buffer offset to readStart \- requestedOffset? If 
the ranges don't fully and contiguously cover the window, we should fall back 
to the single\-buffer path.
   
   





> ABFS: Enable data locality aware reads for Azure Blob Storage
> -------------------------------------------------------------
>
>                 Key: HADOOP-19946
>                 URL: https://issues.apache.org/jira/browse/HADOOP-19946
>             Project: Hadoop Common
>          Issue Type: New Feature
>          Components: fs/azure
>            Reporter: Manish Bhatt
>            Assignee: Manish Bhatt
>            Priority: Major
>              Labels: pull-request-available
>
> Introduce data locality support in ABFS to improve read performance for Azure 
> Blob Storage by routing read requests to the endpoint that hosts the required 
> data range. The goal is to reduce latency, improve throughput, and make read 
> operations more efficient for large files and distributed workloads. This 
> work adds blob layout discovery, range-to-endpoint mapping, locality-aware 
> range selection, and caching of layout metadata to avoid repeated lookups. It 
> also enables segmented read-ahead aligned with locality boundaries and adds 
> safeguards for concurrency, eviction, timeout handling, and failure 
> propagation needed for reliable production-scale reads.



--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

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

Reply via email to