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

roryqi pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/gravitino.git


The following commit(s) were added to refs/heads/main by this push:
     new 0cb2f6db5d [#9468] improvement(client): Add configurable HTTP 
connection pool settings (#9413)
0cb2f6db5d is described below

commit 0cb2f6db5d9e8727f608e1d4146d3ac9b8d1c16e
Author: Junda Yang <[email protected]>
AuthorDate: Mon Dec 15 03:50:22 2025 -0800

    [#9468] improvement(client): Add configurable HTTP connection pool settings 
(#9413)
    
    ### What changes were proposed in this pull request?
    
    Added configurable HTTP connection pool settings for Gravitino Java
    client:
    
    - New configs: gravitino.client.maxConnections (default: 25) and
    gravitino.client.maxConnectionsPerRoute (default: 5)
    - Updated HTTPClient to configure connection manager with these settings
    - Updated GravitinoLanceNamespaceWrapper to pass client configs to
    GravitinoClient
    - Added comprehensive unit tests (9 tests total across configuration and
    integration)
    
    ### Why are the changes needed?
    
    In the auxiliary deployment mode, the default Apache HttpClient 5.x
    settings (5 connections per route) created a significant bottleneck.
    While the Lance server struggled with request queuing and 7-second P75
    latencies for describe-table, the downstream Gravitino server sat idle
    with sub-200ms response times, indicating severe underutilization.
    
    Increasing the HTTP connection pool size immediately rectified this
    imbalance, allowing higher throughput to flow to the Gravitino server
    and shifting the bottleneck to the persistence layer. This demonstrates
    that default HTTP client settings are insufficient for production loads
    and must be tuned to achieve maximum system utility.
    
    Fix: #9468
    
    ### Does this PR introduce _any_ user-facing change?
    
    Yes - Adds two new optional configuration properties:
    
    - gravitino.client.maxConnections - Max total HTTP connections (default:
    200, previously 25)
    - gravitino.client.maxConnectionsPerRoute - Max connections per
    destination (default: 200, previously 5)
    
    For Lance REST server users:
    1. Add these configs to gravitino-lance-rest-server.conf to tune
    performance when running Lance rest server as standalone service.
    2. Add these configs to gravitino.conf to tune performance when running
    Lance rest server as auxiliary service, alongside with the Gravitino
    catalog service.
    
    ### How was this patch tested?
    
    - Added 5 unit tests in TestGravitinoClientConfiguration (defaults,
    custom values, validation, integration)
    - Added 4 unit tests in TestHTTPClient (connection manager
    configuration, high-throughput scenarios)
    - Added 6 unit tests in TestGravitinoLanceNamespaceWrapper (property
    extraction, filtering, edge cases)
---
 .../client/GravitinoClientConfiguration.java       |  81 ++++++-
 .../org/apache/gravitino/client/HTTPClient.java    |   6 +
 .../client/TestGravitinoClientConfiguration.java   |  88 ++++++++
 .../apache/gravitino/client/TestHTTPClient.java    |  97 +++++++++
 .../gravitino/GravitinoLanceNamespaceWrapper.java  |  26 ++-
 .../TestGravitinoLanceNamespaceWrapper.java        | 232 +++++++++++++++++++++
 6 files changed, 527 insertions(+), 3 deletions(-)

diff --git 
a/clients/client-java/src/main/java/org/apache/gravitino/client/GravitinoClientConfiguration.java
 
b/clients/client-java/src/main/java/org/apache/gravitino/client/GravitinoClientConfiguration.java
index 4de877c108..eb61f734c5 100644
--- 
a/clients/client-java/src/main/java/org/apache/gravitino/client/GravitinoClientConfiguration.java
+++ 
b/clients/client-java/src/main/java/org/apache/gravitino/client/GravitinoClientConfiguration.java
@@ -44,8 +44,31 @@ public class GravitinoClientConfiguration {
   /** An optional http socket timeout in milliseconds. */
   public static final String CLIENT_SOCKET_TIMEOUT_MS = 
"gravitino.client.socketTimeoutMs";
 
+  /**
+   * A default value for max total HTTP connections in the connection pool. 
This is the same as the
+   * default value of Apache HttpClient 5.x
+   */
+  public static final int CLIENT_MAX_CONNECTIONS_DEFAULT = 25;
+
+  /** An optional max total HTTP connections. */
+  public static final String CLIENT_MAX_CONNECTIONS = 
"gravitino.client.maxConnections";
+
+  /**
+   * A default value for max HTTP connections per route. This is the same as 
the default value of
+   * Apache HttpClient 5.x
+   */
+  public static final int CLIENT_MAX_CONNECTIONS_PER_ROUTE_DEFAULT = 5;
+
+  /** An optional max HTTP connections per route. */
+  public static final String CLIENT_MAX_CONNECTIONS_PER_ROUTE =
+      "gravitino.client.maxConnectionsPerRoute";
+
   private static final Set<String> SUPPORT_CLIENT_CONFIG_KEYS =
-      ImmutableSet.of(CLIENT_CONNECTION_TIMEOUT_MS, CLIENT_SOCKET_TIMEOUT_MS);
+      ImmutableSet.of(
+          CLIENT_CONNECTION_TIMEOUT_MS,
+          CLIENT_SOCKET_TIMEOUT_MS,
+          CLIENT_MAX_CONNECTIONS,
+          CLIENT_MAX_CONNECTIONS_PER_ROUTE);
 
   private Map<String, String> properties;
 
@@ -65,7 +88,9 @@ public class GravitinoClientConfiguration {
         throw new IllegalArgumentException(String.format("Invalid property for 
client: %s", key));
       }
     }
-    return new GravitinoClientConfiguration(properties);
+    GravitinoClientConfiguration config = new 
GravitinoClientConfiguration(properties);
+    config.validateConnectionPoolSettings();
+    return config;
   }
 
   /**
@@ -102,6 +127,36 @@ public class GravitinoClientConfiguration {
     return socketTimeoutMillis;
   }
 
+  /**
+   * Extract max total connections from the properties map
+   *
+   * @return max total connections in the HTTP connection pool
+   */
+  public int getClientMaxConnections() {
+    int maxConnections =
+        MapUtils.propertyAsInt(properties, CLIENT_MAX_CONNECTIONS, 
CLIENT_MAX_CONNECTIONS_DEFAULT);
+    checkValue(
+        value -> value > 0, CLIENT_MAX_CONNECTIONS, maxConnections, 
POSITIVE_NUMBER_ERROR_MSG);
+    return maxConnections;
+  }
+
+  /**
+   * Extract max connections per route from the properties map
+   *
+   * @return max connections per route in the HTTP connection pool
+   */
+  public int getClientMaxConnectionsPerRoute() {
+    int maxConnectionsPerRoute =
+        MapUtils.propertyAsInt(
+            properties, CLIENT_MAX_CONNECTIONS_PER_ROUTE, 
CLIENT_MAX_CONNECTIONS_PER_ROUTE_DEFAULT);
+    checkValue(
+        value -> value > 0,
+        CLIENT_MAX_CONNECTIONS_PER_ROUTE,
+        maxConnectionsPerRoute,
+        POSITIVE_NUMBER_ERROR_MSG);
+    return maxConnectionsPerRoute;
+  }
+
   private static <T> void checkValue(
       Function<T, Boolean> checkValueFunc, String key, T value, String 
errorMsg) {
     if (!checkValueFunc.apply(value)) {
@@ -109,4 +164,26 @@ public class GravitinoClientConfiguration {
           String.format("%s in %s is invalid. %s", value, key, errorMsg));
     }
   }
+
+  /**
+   * Validate the relationship between connection pool settings. This method 
ensures that the total
+   * maximum connections is greater than or equal to the maximum connections 
per route.
+   *
+   * @throws IllegalArgumentException if maxConnections < 
maxConnectionsPerRoute
+   */
+  private void validateConnectionPoolSettings() {
+    int maxConnections = getClientMaxConnections();
+    int maxConnectionsPerRoute = getClientMaxConnectionsPerRoute();
+
+    if (maxConnections < maxConnectionsPerRoute) {
+      throw new IllegalArgumentException(
+          String.format(
+              "Invalid HTTP connection pool configuration: %s (%d) must be 
greater than or equal to %s (%d). "
+                  + "The total connection pool size cannot be smaller than the 
per-route connection limit.",
+              CLIENT_MAX_CONNECTIONS,
+              maxConnections,
+              CLIENT_MAX_CONNECTIONS_PER_ROUTE,
+              maxConnectionsPerRoute));
+    }
+  }
 }
diff --git 
a/clients/client-java/src/main/java/org/apache/gravitino/client/HTTPClient.java 
b/clients/client-java/src/main/java/org/apache/gravitino/client/HTTPClient.java
index ba961fb296..b5de221c11 100644
--- 
a/clients/client-java/src/main/java/org/apache/gravitino/client/HTTPClient.java
+++ 
b/clients/client-java/src/main/java/org/apache/gravitino/client/HTTPClient.java
@@ -722,6 +722,12 @@ public class HTTPClient implements RESTClient {
       GravitinoClientConfiguration clientConfiguration) {
     PoolingHttpClientConnectionManagerBuilder connectionManagerBuilder =
         PoolingHttpClientConnectionManagerBuilder.create();
+
+    // Configure connection pool size
+    
connectionManagerBuilder.setMaxConnTotal(clientConfiguration.getClientMaxConnections());
+    connectionManagerBuilder.setMaxConnPerRoute(
+        clientConfiguration.getClientMaxConnectionsPerRoute());
+
     ConnectionConfig connectionConfig = 
configureConnectionConfig(clientConfiguration);
     connectionManagerBuilder.setDefaultConnectionConfig(connectionConfig);
     return connectionManagerBuilder.build();
diff --git 
a/clients/client-java/src/test/java/org/apache/gravitino/client/TestGravitinoClientConfiguration.java
 
b/clients/client-java/src/test/java/org/apache/gravitino/client/TestGravitinoClientConfiguration.java
index 36ff730551..20e10ab133 100644
--- 
a/clients/client-java/src/test/java/org/apache/gravitino/client/TestGravitinoClientConfiguration.java
+++ 
b/clients/client-java/src/test/java/org/apache/gravitino/client/TestGravitinoClientConfiguration.java
@@ -50,4 +50,92 @@ public class TestGravitinoClientConfiguration {
     Assertions.assertEquals(
         "Invalid property for client: gravitino.client.xxxx", 
throwable.getMessage());
   }
+
+  @Test
+  void testConnectionPoolDefaults() {
+    // Test default values when properties are not provided
+    Map<String, String> properties = ImmutableMap.of();
+    GravitinoClientConfiguration clientConfiguration =
+        GravitinoClientConfiguration.buildFromProperties(properties);
+
+    Assertions.assertEquals(
+        GravitinoClientConfiguration.CLIENT_MAX_CONNECTIONS_DEFAULT,
+        clientConfiguration.getClientMaxConnections());
+    Assertions.assertEquals(
+        GravitinoClientConfiguration.CLIENT_MAX_CONNECTIONS_PER_ROUTE_DEFAULT,
+        clientConfiguration.getClientMaxConnectionsPerRoute());
+  }
+
+  @Test
+  void testConnectionPoolCustomValues() {
+    // Test custom connection pool values
+    Map<String, String> properties =
+        ImmutableMap.of(
+            GravitinoClientConfiguration.CLIENT_MAX_CONNECTIONS, "400",
+            GravitinoClientConfiguration.CLIENT_MAX_CONNECTIONS_PER_ROUTE, 
"400");
+    GravitinoClientConfiguration clientConfiguration =
+        GravitinoClientConfiguration.buildFromProperties(properties);
+
+    Assertions.assertEquals(400, 
clientConfiguration.getClientMaxConnections());
+    Assertions.assertEquals(400, 
clientConfiguration.getClientMaxConnectionsPerRoute());
+  }
+
+  @Test
+  void testConnectionPoolInvalidNegativeValues() {
+    // Test that negative values are rejected
+    Map<String, String> properties =
+        ImmutableMap.of(GravitinoClientConfiguration.CLIENT_MAX_CONNECTIONS, 
"-1");
+
+    Throwable throwable =
+        Assertions.assertThrows(
+            IllegalArgumentException.class,
+            () -> {
+              GravitinoClientConfiguration config =
+                  GravitinoClientConfiguration.buildFromProperties(properties);
+              config.getClientMaxConnections();
+            });
+    Assertions.assertTrue(throwable.getMessage().contains("The value must be a 
positive number"));
+  }
+
+  @Test
+  void testConnectionPoolInvalidZeroValues() {
+    // Test that zero values are rejected
+    Map<String, String> properties =
+        
ImmutableMap.of(GravitinoClientConfiguration.CLIENT_MAX_CONNECTIONS_PER_ROUTE, 
"0");
+
+    Throwable throwable =
+        Assertions.assertThrows(
+            IllegalArgumentException.class,
+            () -> {
+              GravitinoClientConfiguration config =
+                  GravitinoClientConfiguration.buildFromProperties(properties);
+              config.getClientMaxConnectionsPerRoute();
+            });
+    Assertions.assertTrue(throwable.getMessage().contains("The value must be a 
positive number"));
+  }
+
+  @Test
+  void testConnectionPoolValidation_InvalidConfiguration() {
+    // Test invalid configuration: maxConnections < maxConnectionsPerRoute
+    Map<String, String> properties =
+        ImmutableMap.of(
+            GravitinoClientConfiguration.CLIENT_MAX_CONNECTIONS, "50",
+            GravitinoClientConfiguration.CLIENT_MAX_CONNECTIONS_PER_ROUTE, 
"100");
+
+    Throwable throwable =
+        Assertions.assertThrows(
+            IllegalArgumentException.class,
+            () -> 
GravitinoClientConfiguration.buildFromProperties(properties));
+
+    Assertions.assertTrue(
+        throwable
+            .getMessage()
+            .contains(
+                "gravitino.client.maxConnections (50) must be greater than or 
equal to gravitino.client.maxConnectionsPerRoute (100)"));
+    Assertions.assertTrue(
+        throwable
+            .getMessage()
+            .contains(
+                "The total connection pool size cannot be smaller than the 
per-route connection limit"));
+  }
 }
diff --git 
a/clients/client-java/src/test/java/org/apache/gravitino/client/TestHTTPClient.java
 
b/clients/client-java/src/test/java/org/apache/gravitino/client/TestHTTPClient.java
index c1ff31a820..64b270bc91 100644
--- 
a/clients/client-java/src/test/java/org/apache/gravitino/client/TestHTTPClient.java
+++ 
b/clients/client-java/src/test/java/org/apache/gravitino/client/TestHTTPClient.java
@@ -202,6 +202,103 @@ public class TestHTTPClient {
     }
   }
 
+  @Test
+  public void testConnectionPoolDefaults() {
+    // Test that HTTPClient can be built with default connection pool settings
+    Map<String, String> emptyProps = ImmutableMap.of();
+    GravitinoClientConfiguration config =
+        GravitinoClientConfiguration.buildFromProperties(emptyProps);
+
+    Assertions.assertEquals(
+        GravitinoClientConfiguration.CLIENT_MAX_CONNECTIONS_DEFAULT,
+        config.getClientMaxConnections());
+    Assertions.assertEquals(
+        GravitinoClientConfiguration.CLIENT_MAX_CONNECTIONS_PER_ROUTE_DEFAULT,
+        config.getClientMaxConnectionsPerRoute());
+
+    // Verify HTTPClient can be built with these defaults
+    HTTPClient client =
+        HTTPClient.builder(emptyProps)
+            .uri(String.format("http://127.0.0.1:%d";, mockServer.getPort()))
+            .build();
+    Assertions.assertNotNull(client);
+  }
+
+  @Test
+  public void testConnectionPoolCustomValues() {
+    // Test that HTTPClient can be built with custom connection pool settings
+    Map<String, String> properties =
+        ImmutableMap.of(
+            GravitinoClientConfiguration.CLIENT_MAX_CONNECTIONS,
+            "400",
+            GravitinoClientConfiguration.CLIENT_MAX_CONNECTIONS_PER_ROUTE,
+            "400");
+
+    GravitinoClientConfiguration config =
+        GravitinoClientConfiguration.buildFromProperties(properties);
+
+    Assertions.assertEquals(400, config.getClientMaxConnections());
+    Assertions.assertEquals(400, config.getClientMaxConnectionsPerRoute());
+
+    // Verify HTTPClient can be built with custom values
+    HTTPClient client =
+        HTTPClient.builder(properties)
+            .uri(String.format("http://127.0.0.1:%d";, mockServer.getPort()))
+            .build();
+    Assertions.assertNotNull(client);
+  }
+
+  @Test
+  public void testConnectionPoolHighThroughputConfig() {
+    // Test configuration for high-throughput scenario (like Lance REST server)
+    Map<String, String> properties =
+        ImmutableMap.<String, String>builder()
+            .put(GravitinoClientConfiguration.CLIENT_MAX_CONNECTIONS, "800")
+            
.put(GravitinoClientConfiguration.CLIENT_MAX_CONNECTIONS_PER_ROUTE, "800")
+            .put(GravitinoClientConfiguration.CLIENT_CONNECTION_TIMEOUT_MS, 
"5000")
+            .put(GravitinoClientConfiguration.CLIENT_SOCKET_TIMEOUT_MS, 
"30000")
+            .build();
+
+    GravitinoClientConfiguration config =
+        GravitinoClientConfiguration.buildFromProperties(properties);
+
+    Assertions.assertEquals(800, config.getClientMaxConnections());
+    Assertions.assertEquals(800, config.getClientMaxConnectionsPerRoute());
+    Assertions.assertEquals(5000L, config.getClientConnectionTimeoutMs());
+    Assertions.assertEquals(30000, config.getClientSocketTimeoutMs());
+
+    // Verify HTTPClient can be built with high-throughput settings
+    HTTPClient client =
+        HTTPClient.builder(properties)
+            .uri(String.format("http://127.0.0.1:%d";, mockServer.getPort()))
+            .build();
+    Assertions.assertNotNull(client);
+  }
+
+  @Test
+  public void testConnectionPoolSingleRoutePattern() {
+    // Test the recommended pattern for single-route scenarios (maxPerRoute = 
maxTotal)
+    Map<String, String> properties =
+        ImmutableMap.of(
+            GravitinoClientConfiguration.CLIENT_MAX_CONNECTIONS,
+            "200",
+            GravitinoClientConfiguration.CLIENT_MAX_CONNECTIONS_PER_ROUTE,
+            "200");
+
+    GravitinoClientConfiguration config =
+        GravitinoClientConfiguration.buildFromProperties(properties);
+
+    // For single-route clients, maxPerRoute should equal maxTotal
+    Assertions.assertEquals(
+        config.getClientMaxConnections(), 
config.getClientMaxConnectionsPerRoute());
+
+    HTTPClient client =
+        HTTPClient.builder(properties)
+            .uri(String.format("http://127.0.0.1:%d";, mockServer.getPort()))
+            .build();
+    Assertions.assertNotNull(client);
+  }
+
   @Test
   public void testConnectionTimeout() throws IOException {
     String path = "test_connection_timeout";
diff --git 
a/lance/lance-common/src/main/java/org/apache/gravitino/lance/common/ops/gravitino/GravitinoLanceNamespaceWrapper.java
 
b/lance/lance-common/src/main/java/org/apache/gravitino/lance/common/ops/gravitino/GravitinoLanceNamespaceWrapper.java
index d90bf08196..0a48dd01e3 100644
--- 
a/lance/lance-common/src/main/java/org/apache/gravitino/lance/common/ops/gravitino/GravitinoLanceNamespaceWrapper.java
+++ 
b/lance/lance-common/src/main/java/org/apache/gravitino/lance/common/ops/gravitino/GravitinoLanceNamespaceWrapper.java
@@ -25,6 +25,8 @@ import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Preconditions;
 import com.lancedb.lance.namespace.LanceNamespaceException;
 import com.lancedb.lance.namespace.util.CommonUtil;
+import java.util.HashMap;
+import java.util.Map;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.gravitino.Catalog;
 import org.apache.gravitino.client.GravitinoClient;
@@ -65,7 +67,29 @@ public class GravitinoLanceNamespaceWrapper extends 
NamespaceWrapper {
         StringUtils.isNotBlank(metalakeName),
         "Metalake name must be provided for Lance Gravitino namespace 
backend");
 
-    this.client = 
GravitinoClient.builder(uri).withMetalake(metalakeName).build();
+    // Extract client configuration properties (e.g., connection pool settings)
+    Map<String, String> clientProperties = new HashMap<>();
+    config()
+        .getAllConfig()
+        .forEach(
+            (key, value) -> {
+              if (key.startsWith("gravitino.client.")) {
+                clientProperties.put(key, value);
+                LOG.info("Applying client config: {} = {}", key, value);
+              }
+            });
+
+    this.client =
+        GravitinoClient.builder(uri)
+            .withMetalake(metalakeName)
+            .withClientConfig(clientProperties)
+            .build();
+
+    LOG.info(
+        "GravitinoClient initialized with {} client properties for metalake: 
{}",
+        clientProperties.size(),
+        metalakeName);
+
     this.namespaceOperations = new GravitinoLanceNameSpaceOperations(this);
     this.tableOperations = new GravitinoLanceTableOperations(this);
   }
diff --git 
a/lance/lance-common/src/test/java/org/apache/gravitino/lance/common/ops/gravitino/TestGravitinoLanceNamespaceWrapper.java
 
b/lance/lance-common/src/test/java/org/apache/gravitino/lance/common/ops/gravitino/TestGravitinoLanceNamespaceWrapper.java
new file mode 100644
index 0000000000..6eb3fe2c74
--- /dev/null
+++ 
b/lance/lance-common/src/test/java/org/apache/gravitino/lance/common/ops/gravitino/TestGravitinoLanceNamespaceWrapper.java
@@ -0,0 +1,232 @@
+/*
+ * 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.gravitino.lance.common.ops.gravitino;
+
+import com.google.common.collect.ImmutableMap;
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.gravitino.lance.common.config.LanceConfig;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class TestGravitinoLanceNamespaceWrapper {
+
+  @Test
+  public void testClientPropertiesExtraction() {
+    // Test that client properties are correctly extracted from LanceConfig
+    Map<String, String> properties =
+        ImmutableMap.<String, String>builder()
+            .put(LanceConfig.NAMESPACE_BACKEND_URI.getKey(), 
"http://localhost:8090";)
+            .put(LanceConfig.METALAKE_NAME.getKey(), "test_metalake")
+            .put("gravitino.client.maxConnections", "400")
+            .put("gravitino.client.maxConnectionsPerRoute", "400")
+            .put("gravitino.client.connectionTimeoutMs", "5000")
+            .put("gravitino.client.socketTimeoutMs", "30000")
+            .build();
+
+    LanceConfig lanceConfig = new LanceConfig(properties);
+
+    // Extract client properties (same logic as in initialize())
+    Map<String, String> clientProperties = new HashMap<>();
+    lanceConfig
+        .getAllConfig()
+        .forEach(
+            (key, value) -> {
+              if (key.startsWith("gravitino.client.")) {
+                clientProperties.put(key, value);
+              }
+            });
+
+    // Verify all client properties are extracted
+    Assertions.assertEquals(4, clientProperties.size());
+    Assertions.assertEquals("400", 
clientProperties.get("gravitino.client.maxConnections"));
+    Assertions.assertEquals("400", 
clientProperties.get("gravitino.client.maxConnectionsPerRoute"));
+    Assertions.assertEquals("5000", 
clientProperties.get("gravitino.client.connectionTimeoutMs"));
+    Assertions.assertEquals("30000", 
clientProperties.get("gravitino.client.socketTimeoutMs"));
+
+    // Verify non-client properties are not included
+    Assertions.assertFalse(
+        
clientProperties.containsKey(LanceConfig.NAMESPACE_BACKEND_URI.getKey()));
+    
Assertions.assertFalse(clientProperties.containsKey(LanceConfig.METALAKE_NAME.getKey()));
+  }
+
+  @Test
+  public void testClientPropertiesWithDefaults() {
+    // Test that initialization works without client properties
+    Map<String, String> properties =
+        ImmutableMap.<String, String>builder()
+            .put(LanceConfig.NAMESPACE_BACKEND_URI.getKey(), 
"http://localhost:8090";)
+            .put(LanceConfig.METALAKE_NAME.getKey(), "test_metalake")
+            .build();
+
+    LanceConfig lanceConfig = new LanceConfig(properties);
+
+    // Extract client properties (same logic as in initialize())
+    Map<String, String> clientProperties = new HashMap<>();
+    lanceConfig
+        .getAllConfig()
+        .forEach(
+            (key, value) -> {
+              if (key.startsWith("gravitino.client.")) {
+                clientProperties.put(key, value);
+              }
+            });
+
+    // Should be empty (no client properties provided)
+    Assertions.assertEquals(0, clientProperties.size());
+  }
+
+  @Test
+  public void testClientPropertiesPartialConfig() {
+    // Test with only some client properties
+    Map<String, String> properties =
+        ImmutableMap.<String, String>builder()
+            .put(LanceConfig.NAMESPACE_BACKEND_URI.getKey(), 
"http://localhost:8090";)
+            .put(LanceConfig.METALAKE_NAME.getKey(), "test_metalake")
+            .put("gravitino.client.maxConnections", "200")
+            // Only maxConnections, not maxConnectionsPerRoute
+            .build();
+
+    LanceConfig lanceConfig = new LanceConfig(properties);
+
+    // Extract client properties
+    Map<String, String> clientProperties = new HashMap<>();
+    lanceConfig
+        .getAllConfig()
+        .forEach(
+            (key, value) -> {
+              if (key.startsWith("gravitino.client.")) {
+                clientProperties.put(key, value);
+              }
+            });
+
+    // Should only have the one property
+    Assertions.assertEquals(1, clientProperties.size());
+    Assertions.assertEquals("200", 
clientProperties.get("gravitino.client.maxConnections"));
+    
Assertions.assertNull(clientProperties.get("gravitino.client.maxConnectionsPerRoute"));
+  }
+
+  @Test
+  public void testClientPropertiesFiltering() {
+    // Test that properties with similar names but different prefixes are not 
included
+    Map<String, String> properties =
+        ImmutableMap.<String, String>builder()
+            .put(LanceConfig.NAMESPACE_BACKEND_URI.getKey(), 
"http://localhost:8090";)
+            .put(LanceConfig.METALAKE_NAME.getKey(), "test_metalake")
+            .put("gravitino.client.maxConnections", "400")
+            .put("gravitino.server.maxThreads", "800") // Should NOT be 
included
+            .put("gravitino.cache.maxEntries", "10000") // Should NOT be 
included
+            .put("gravitino.client.connectionTimeoutMs", "5000")
+            .put("some.other.client.property", "value") // Should NOT be 
included
+            .build();
+
+    LanceConfig lanceConfig = new LanceConfig(properties);
+
+    // Extract client properties
+    Map<String, String> clientProperties = new HashMap<>();
+    lanceConfig
+        .getAllConfig()
+        .forEach(
+            (key, value) -> {
+              if (key.startsWith("gravitino.client.")) {
+                clientProperties.put(key, value);
+              }
+            });
+
+    // Only properties with "gravitino.client." prefix should be included
+    Assertions.assertEquals(2, clientProperties.size());
+    Assertions.assertEquals("400", 
clientProperties.get("gravitino.client.maxConnections"));
+    Assertions.assertEquals("5000", 
clientProperties.get("gravitino.client.connectionTimeoutMs"));
+
+    // Verify others are excluded
+    
Assertions.assertFalse(clientProperties.containsKey("gravitino.server.maxThreads"));
+    
Assertions.assertFalse(clientProperties.containsKey("gravitino.cache.maxEntries"));
+    
Assertions.assertFalse(clientProperties.containsKey("some.other.client.property"));
+  }
+
+  @Test
+  public void testHighThroughputConfiguration() {
+    // Test high-throughput scenario (like Lance REST server with 800 threads)
+    Map<String, String> properties =
+        ImmutableMap.<String, String>builder()
+            .put(LanceConfig.NAMESPACE_BACKEND_URI.getKey(), 
"http://gravitino-prod:8090";)
+            .put(LanceConfig.METALAKE_NAME.getKey(), "production")
+            .put("gravitino.client.maxConnections", "800")
+            .put("gravitino.client.maxConnectionsPerRoute", "800")
+            .put("gravitino.client.connectionTimeoutMs", "5000")
+            .put("gravitino.client.socketTimeoutMs", "30000")
+            .build();
+
+    LanceConfig lanceConfig = new LanceConfig(properties);
+
+    // Verify Lance-specific config
+    Assertions.assertEquals("http://gravitino-prod:8090";, 
lanceConfig.getNamespaceBackendUri());
+    Assertions.assertEquals("production", lanceConfig.getGravitinoMetalake());
+
+    // Extract and verify client properties
+    Map<String, String> clientProperties = new HashMap<>();
+    lanceConfig
+        .getAllConfig()
+        .forEach(
+            (key, value) -> {
+              if (key.startsWith("gravitino.client.")) {
+                clientProperties.put(key, value);
+              }
+            });
+
+    // Verify high-throughput settings
+    Assertions.assertEquals(4, clientProperties.size());
+    Assertions.assertEquals("800", 
clientProperties.get("gravitino.client.maxConnections"));
+    Assertions.assertEquals("800", 
clientProperties.get("gravitino.client.maxConnectionsPerRoute"));
+
+    // Verify single-route pattern (maxConnections == maxConnectionsPerRoute)
+    Assertions.assertEquals(
+        clientProperties.get("gravitino.client.maxConnections"),
+        clientProperties.get("gravitino.client.maxConnectionsPerRoute"));
+  }
+
+  @Test
+  public void testLanceConfigWithAllClientProperties() {
+    // Test complete client configuration
+    Map<String, String> properties =
+        ImmutableMap.<String, String>builder()
+            .put(LanceConfig.NAMESPACE_BACKEND_URI.getKey(), 
"http://localhost:8090";)
+            .put(LanceConfig.METALAKE_NAME.getKey(), "test_metalake")
+            .put("gravitino.client.maxConnections", "400")
+            .put("gravitino.client.maxConnectionsPerRoute", "400")
+            .put("gravitino.client.connectionTimeoutMs", "10000")
+            .put("gravitino.client.socketTimeoutMs", "60000")
+            .build();
+
+    LanceConfig lanceConfig = new LanceConfig(properties);
+
+    // Verify all properties are accessible
+    Map<String, String> allConfig = lanceConfig.getAllConfig();
+    
Assertions.assertTrue(allConfig.containsKey("gravitino.client.maxConnections"));
+    
Assertions.assertTrue(allConfig.containsKey("gravitino.client.maxConnectionsPerRoute"));
+    
Assertions.assertTrue(allConfig.containsKey("gravitino.client.connectionTimeoutMs"));
+    
Assertions.assertTrue(allConfig.containsKey("gravitino.client.socketTimeoutMs"));
+
+    // Verify values
+    Assertions.assertEquals("400", 
allConfig.get("gravitino.client.maxConnections"));
+    Assertions.assertEquals("400", 
allConfig.get("gravitino.client.maxConnectionsPerRoute"));
+    Assertions.assertEquals("10000", 
allConfig.get("gravitino.client.connectionTimeoutMs"));
+    Assertions.assertEquals("60000", 
allConfig.get("gravitino.client.socketTimeoutMs"));
+  }
+}

Reply via email to