Copilot commented on code in PR #12857:
URL: https://github.com/apache/gravitino/pull/12857#discussion_r3915486192


##########
clients/filesystem-hadoop3/src/main/java/org/apache/gravitino/filesystem/hadoop/BaseGVFSOperations.java:
##########
@@ -962,14 +1072,42 @@ private Cache<FileSystemCacheKey, FileSystem> 
newFileSystemCache(Configuration c
     return cacheBuilder.build();
   }
 
+  /**
+   * The properties that identify which {@link FileSystem} instance a path 
belongs to: the catalog
+   * and fileset properties plus this filesystem's own configuration. Every 
value here is already in
+   * hand once the fileset is loaded, so building this costs no server call.
+   *
+   * <p>Kept separate from {@link #getAllProperties} because {@link
+   * FileSystemProvider#getFullAuthority} reads from it to form the filesystem 
cache key, which has
+   * to happen before the cache is consulted -- and therefore on every single 
operation. Secrets are
+   * deliberately not resolved here: each {@code getSecrets()} is a REST call, 
and no provider
+   * derives an authority from a secret.
+   */
+  private Map<String, String> getIdentityProperties(Fileset fileset, Catalog 
catalog) {
+    Map<String, String> properties = new HashMap<>();
+    if (catalog.properties() != null) {
+      properties.putAll(catalog.properties());
+    }
+    if (fileset.properties() != null) {
+      properties.putAll(fileset.properties());
+    }
+    properties.putAll(extractNonDefaultConfig(conf));
+    return properties;
+  }
+
+  /**
+   * Everything needed to construct a {@link FileSystem}: the schema's 
properties, and the secrets
+   * of the catalog, schema and fileset.
+   *
+   * <p>Loading the schema is a server call when the metadata cache is off, 
and each {@code
+   * getSecrets()} is another one that no cache absorbs. The result is only 
read when the filesystem
+   * cache misses -- once per scheme, authority and user per JVM. Callers 
therefore pass this as a
+   * supplier rather than a value; see {@link #getActualFileSystemByPath}.
+   */
   @VisibleForTesting
-  Map<String, String> getAllProperties(NameIdentifier filesetIdent) {
-    String catalogName = filesetIdent.namespace().level(1);
-    String schemaName = filesetIdent.namespace().level(2);
-    Catalog catalog = getGravitinoClient().loadCatalog(catalogName);
-    Schema schema = catalog.asSchemas().loadSchema(schemaName);
-    Fileset fileset =
-        catalog.asFilesetCatalog().loadFileset(NameIdentifier.of(schemaName, 
filesetIdent.name()));
+  Map<String, String> getAllProperties(
+      NameIdentifier filesetIdent, Fileset fileset, Catalog catalog) {
+    Schema schema = 
getSchema(NameIdentifier.parse(filesetIdent.namespace().toString()), catalog);

Review Comment:
   `NameIdentifier.parse(filesetIdent.namespace().toString())` relies on the 
`toString()` format of `Namespace`, which is not guaranteed to be a parseable 
identifier and can also produce identifiers inconsistent with the rest of the 
codebase (leading to cache misses and extra REST calls). Construct the schema 
identifier explicitly from the namespace levels (metalake/catalog/schema) 
rather than round-tripping through `toString()`.



##########
clients/filesystem-hadoop3/src/main/java/org/apache/gravitino/filesystem/hadoop/DefaultGVFSOperations.java:
##########
@@ -124,32 +129,31 @@ public boolean rename(Path srcGvfsPath, Path dstGvfsPath) 
throws IOException {
         srcIdentifier,
         dstIdentifier);
 
-    Path srcActualPath =
-        getActualFilePath(srcGvfsPath, currentLocationName(), 
FilesetDataOperation.RENAME);
+    Pair<FileSystem, Path> src =
+        resolvePath(srcGvfsPath, currentLocationName(), 
FilesetDataOperation.RENAME);
+    // Both paths are in the same fileset, asserted above, so the source's 
filesystem serves both.
     Path dstActualPath =
         getActualFilePath(dstGvfsPath, currentLocationName(), 
FilesetDataOperation.RENAME);
-    FileSystem actualFs = getActualFileSystem(srcGvfsPath, 
currentLocationName());
-    return actualFs.rename(srcActualPath, dstActualPath);
+    return src.getLeft().rename(src.getRight(), dstActualPath);

Review Comment:
   In `rename`, `resolvePath(...)` loads the catalog once, but 
`getActualFilePath(...)` will load the same catalog again for the destination 
path. This leaves `rename` with the same duplicated catalog lookup pattern the 
PR is trying to eliminate. Consider adding a protected overload/helper (similar 
to the new `getFileset(..., FilesetCatalog)`/`getSchema(..., Catalog)`) that 
can compute the actual path using an already-resolved `FilesetCatalog` (and 
possibly `Fileset`) so both src/dst use a single catalog load.



##########
clients/filesystem-hadoop3/src/test/java/org/apache/gravitino/filesystem/hadoop/TestGvfsBase.java:
##########
@@ -1755,4 +1755,58 @@ private void buildMockResourceForCredential(String 
filesetName, String filesetLo
   private MockGVFSHook getHook(FileSystem gvfs) {
     return (MockGVFSHook) ((GravitinoVirtualFileSystem) gvfs).getHook();
   }
+
+  @Test
+  public void testOneCatalogLoadPerOperation() throws IOException {
+    Assumptions.assumeTrue(getClass() == TestGvfsBase.class);
+    String filesetName = "testOneCatalogLoadPerOperation";
+    Path managedFilesetPath =
+        FileSystemTestUtils.createFilesetPath(catalogName, schemaName, 
filesetName, true);
+    Path localPath = FileSystemTestUtils.createLocalDirPrefix(catalogName, 
schemaName, filesetName);
+    String catalogPath = "/api/metalakes/" + metalakeName + "/catalogs/" + 
catalogName;
+    String locationPath =
+        String.format(
+            "/api/metalakes/%s/catalogs/%s/schemas/%s/filesets/%s/location",
+            metalakeName, catalogName, schemaName, 
RESTUtils.encodeString(filesetName));
+
+    try (FileSystem gravitinoFileSystem = 
managedFilesetPath.getFileSystem(conf);
+        FileSystem localFileSystem = localPath.getFileSystem(conf)) {
+      FileSystemTestUtils.mkdirs(localPath, localFileSystem);
+      mockFilesetDTO(
+          metalakeName,
+          catalogName,
+          schemaName,
+          filesetName,
+          Fileset.Type.MANAGED,
+          ImmutableMap.of("location1", localPath.toString()),
+          ImmutableMap.of(PROPERTY_DEFAULT_LOCATION_NAME, "location1"));
+      Map<String, String> queryParams = new HashMap<>();
+      queryParams.put("sub_path", "/test.txt");
+      buildMockResource(
+          Method.GET,
+          locationPath,
+          queryParams,
+          null,
+          new FileLocationResponse(localPath + "/test.txt"),
+          SC_OK);
+      buildMockResourceForCredential(filesetName, localPath + "/test.txt");
+
+      // Warm the filesystem cache first: the very first operation 
additionally constructs a
+      // FileSystem, which is what the deferred property map is for. Steady 
state is the interesting
+      // number -- it is the one a query engine pays once per file.
+      Path filePath = new Path(managedFilesetPath + "/test.txt");
+      FileSystemTestUtils.create(filePath, gravitinoFileSystem);
+
+      HttpRequest catalogRequest = HttpRequest.request(catalogPath);
+      int before = 
mockServer().retrieveRecordedRequests(catalogRequest).length;
+      FileSystemTestUtils.create(filePath, gravitinoFileSystem);
+      int after = mockServer().retrieveRecordedRequests(catalogRequest).length;
+
+      // Before this was collapsed, a single create resolved the catalog four 
separate times.
+      assertEquals(
+          1, after - before, "one filesystem operation must load the catalog 
exactly once");

Review Comment:
   This assertion is likely overly strict for the stated goal (collapsing 
duplicates). If a future optimization removes the catalog call entirely on 
steady-state cache hits, `after - before` could become `0` (an improvement) and 
this test would fail. If the intent is ‘no more than once’, assert `<= 1`; if 
the intent is ‘exactly once’, make the test explicitly configure/disable 
metadata caching such that one catalog load is required, so the expectation is 
stable.



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

Reply via email to