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

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

bhattmanish98 commented on code in PR #8611:
URL: https://github.com/apache/hadoop/pull/8611#discussion_r3850272151


##########
hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azurebfs/ITestAbfsPhotonListStatus.java:
##########
@@ -0,0 +1,249 @@
+/**
+ * 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.fs.azurebfs;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.FSDataOutputStream;
+import org.apache.hadoop.fs.FileStatus;
+import org.apache.hadoop.fs.FileSystem;
+import org.apache.hadoop.fs.LocatedFileStatus;
+import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.fs.RemoteIterator;
+
+import static 
org.apache.hadoop.fs.azurebfs.AbfsStatistic.PHOTON_FALLBACK_COUNT;
+import static 
org.apache.hadoop.fs.azurebfs.AbfsStatistic.PHOTON_LISTING_LATENCY;
+import static 
org.apache.hadoop.fs.azurebfs.AbfsStatistic.PHOTON_PARSE_FAILURE_COUNT;
+import static org.apache.hadoop.fs.azurebfs.AbfsStatistic.PHOTON_REQUEST_COUNT;
+import static 
org.apache.hadoop.fs.azurebfs.AbfsStatistic.PHOTON_RESPONSE_COUNT;
+import static 
org.apache.hadoop.fs.azurebfs.constants.ConfigurationKeys.AZURE_LIST_MAX_RESULTS;
+import static 
org.apache.hadoop.fs.azurebfs.constants.ConfigurationKeys.FS_AZURE_ENABLE_PHOTON;
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Live integration tests for the Photon (Apache Arrow based ListBlob) listing
+ * path against a real Blob-endpoint account. These cover the integration
+ * scenarios from the design document that are not verifiable with unit tests:
+ * <ul>
+ *   <li>XML-vs-Photon {@link FileStatus} parity (identical results regardless 
of
+ *   the wire format).</li>
+ *   <li>Graceful XML fallback when Arrow is requested but the account returns
+ *   XML (asserted via the Photon telemetry classification).</li>
+ *   <li>Pagination across multiple Photon responses.</li>
+ *   <li>Photon telemetry counters and listing-latency tracker are 
emitted.</li>
+ * </ul>
+ *
+ * <p>Photon is only offered on the Blob endpoint, so every test asserts a Blob
+ * service type before running.</p>
+ */
+public class ITestAbfsPhotonListStatus extends AbstractAbfsIntegrationTest {
+
+  private static final Logger LOG =
+      LoggerFactory.getLogger(ITestAbfsPhotonListStatus.class);
+
+  private static final String[] CHILD_FILES = {
+      "file-a.txt",
+      "file-b.txt",
+      "name with space.txt",
+      "\u6587\u4ef6-unicode.txt",
+  };
+
+  private static final String CHILD_DIR = "subdir";
+
+  public ITestAbfsPhotonListStatus() throws Exception {
+  }
+
+  /**
+   * Create a filesystem instance with Photon explicitly toggled and, 
optionally,
+   * a reduced ListBlobs page size to force multi-page pagination.
+   */
+  private AzureBlobFileSystem newFileSystem(final boolean photonEnabled,
+      final int listMaxResults) throws IOException {
+    Configuration conf = new Configuration(getRawConfiguration());
+    conf.setBoolean(FS_AZURE_ENABLE_PHOTON, photonEnabled);
+    if (listMaxResults > 0) {
+      conf.setInt(AZURE_LIST_MAX_RESULTS, listMaxResults);
+    }
+    return (AzureBlobFileSystem) FileSystem.newInstance(conf);
+  }
+
+  /**
+   * Populate a directory with a mix of files (including special-character and
+   * Unicode names) and a subdirectory, so a listing exercises files and
+   * directories together.
+   */
+  private void createTree(final AzureBlobFileSystem fs, final Path baseDir)
+      throws IOException {
+    fs.mkdirs(baseDir);
+    for (int i = 0; i < CHILD_FILES.length; i++) {
+      try (FSDataOutputStream out = fs.create(new Path(baseDir, 
CHILD_FILES[i]))) {
+        // Give the files distinct, non-zero sizes to make parity meaningful.
+        out.write(new byte[i + 1]);
+      }
+    }
+    fs.mkdirs(new Path(baseDir, CHILD_DIR));
+  }
+
+  private static List<FileStatus> sortedByName(final FileStatus[] statuses) {
+    return Arrays.stream(statuses)
+        .sorted(Comparator.comparing(s -> s.getPath().getName()))
+        .collect(Collectors.toList());
+  }
+
+  /**
+   * Verify that listing the same directory over XML (Photon disabled) and over
+   * Arrow (Photon enabled) yields identical {@link FileStatus} results, 
covering
+   * the doc's XML/Photon parity and identical-FileStatus scenarios.
+   */
+  @Test
+  public void testPhotonAndXmlListingParity() throws Exception {
+    assumeBlobServiceType();
+    Path baseDir = path("photonParity-" + getMethodName());
+    createTree(getFileSystem(), baseDir);
+
+    try (AzureBlobFileSystem xmlFs = newFileSystem(false, -1);
+         AzureBlobFileSystem photonFs = newFileSystem(true, -1)) {
+
+      List<FileStatus> xmlStatuses = sortedByName(xmlFs.listStatus(baseDir));
+      List<FileStatus> photonStatuses = 
sortedByName(photonFs.listStatus(baseDir));
+
+      assertThat(photonStatuses)
+          .as("Photon and XML listings must return the same number of entries")
+          .hasSameSizeAs(xmlStatuses);
+
+      for (int i = 0; i < xmlStatuses.size(); i++) {
+        FileStatus xml = xmlStatuses.get(i);
+        FileStatus photon = photonStatuses.get(i);
+        assertThat(photon.getPath())
+            .as("path parity for entry %d", i)
+            .isEqualTo(xml.getPath());
+        assertThat(photon.isDirectory())
+            .as("isDirectory parity for %s", xml.getPath())
+            .isEqualTo(xml.isDirectory());
+        assertThat(photon.getLen())
+            .as("length parity for %s", xml.getPath())
+            .isEqualTo(xml.getLen());
+      }
+    }
+  }
+
+  /**
+   * Verify that a recursive {@code listFiles} over an empty directory (one 
whose
+   * only child is an empty sub-directory marker blob) returns no files on both
+   * the XML and Photon (Arrow) paths. This is the exact scenario behind
+   * {@code testListFilesEmptyDirectoryRecursive}: the Arrow parser must 
classify
+   * the {@code hdi_isfolder=true} marker blob as a directory - not a file - so
+   * the recursive listing yields zero files, matching XML.
+   */
+  @Test
+  public void testPhotonRecursiveListFilesEmptyDirectoryParity() throws 
Exception {
+    assumeBlobServiceType();
+    Path baseDir = path("photonEmptyRecursive-" + getMethodName());
+    FileSystem fs = getFileSystem();
+    fs.mkdirs(baseDir);
+    // The only entry under baseDir is an empty sub-directory marker blob.
+    fs.mkdirs(new Path(baseDir, CHILD_DIR));
+
+    try (AzureBlobFileSystem xmlFs = newFileSystem(false, -1);
+         AzureBlobFileSystem photonFs = newFileSystem(true, -1)) {
+
+      List<Path> xmlFiles = recursiveFilePaths(xmlFs, baseDir);
+      List<Path> photonFiles = recursiveFilePaths(photonFs, baseDir);
+
+      assertThat(xmlFiles)
+          .as("XML recursive listFiles over an empty directory must find no 
files")
+          .isEmpty();
+      assertThat(photonFiles)
+          .as("Photon recursive listFiles must match XML and find no files")
+          .isEqualTo(xmlFiles);
+    }
+  }
+
+  /**
+   * Collect the paths returned by a recursive {@code listFiles}, sorted for a
+   * stable comparison.
+   */
+  private static List<Path> recursiveFilePaths(final FileSystem fs,
+      final Path dir) throws IOException {
+    List<Path> paths = new ArrayList<>();
+    RemoteIterator<LocatedFileStatus> it = fs.listFiles(dir, true);
+    while (it.hasNext()) {
+      paths.add(it.next().getPath());
+    }
+    paths.sort(Comparator.naturalOrder());
+    return paths;
+  }
+
+  /**
+   * Verify that a Photon-enabled listing emits the telemetry counters and the
+   * listing-latency tracker, that every request is classified as exactly one 
of
+   * Arrow response or XML fallback (covering graceful fallback), that 
pagination
+   * across multiple Photon responses works, and that no parse failures occur.
+   */
+  @Test
+  public void testPhotonListingMetricsEmitted() throws Exception {
+    assumeBlobServiceType();
+    Path baseDir = path("photonMetrics-" + getMethodName());
+    createTree(getFileSystem(), baseDir);
+
+    // Force small pages so the listing spans multiple Photon responses.
+    try (AzureBlobFileSystem photonFs = newFileSystem(true, 2)) {

Review Comment:
   Now asserts requests == ceil(totalEntries / pageSize) (5 entries at page 
size 2 = 3 pages) and splits the conflated message: registration/presence, 
exact page count, and Arrow-or-fallback classification are asserted separately.





> ABFS: Support Photon (Apache Arrow) based ListBlobs on Blob endpoint with XML 
> fallback
> --------------------------------------------------------------------------------------
>
>                 Key: HADOOP-19941
>                 URL: https://issues.apache.org/jira/browse/HADOOP-19941
>             Project: Hadoop Common
>          Issue Type: New Feature
>          Components: fs/azure
>            Reporter: Manish Bhatt
>            Assignee: Manish Bhatt
>            Priority: Major
>              Labels: pull-request-available
>
> Add config-gated support (fs.azure.photon.enabled, default off) for consuming 
> ListBlobs responses in the Apache Arrow (Photon) format on the ABFS Blob 
> endpoint. When enabled, ABFS advertises Arrow via an Accept header; the 
> response Content-Type selects an Arrow or the existing XML parser, both 
> producing identical FileStatus results, so downstream behaviour is unchanged. 
> Includes automatic, transparent fallback to XML, full parsing parity 
> (metadata, directory markers, implicit directories, copy properties, native 
> timestamp/length vectors), interrupt-safe Arrow parsing, and Photon telemetry 
> (request, response, fallback, parse-failure counts and listing latency). No 
> public API changes. Covered by unit and integration tests.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

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

Reply via email to