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 4d01ec48f0 [#9802] feat(authz): support preload table metadata in 
batch metadata authorization (#9803)
4d01ec48f0 is described below

commit 4d01ec48f0de77d102eadf83450fb9baf2f5e412
Author: yangyang zhong <[email protected]>
AuthorDate: Fri Jan 30 14:32:56 2026 +0800

    [#9802] feat(authz): support preload table metadata in batch metadata 
authorization (#9803)
    
    ### What changes were proposed in this pull request?
    
    support preload table metadata in batch metadata authorization
    
    ### Why are the changes needed?
    
    Fix: #9802
    
    ### Does this PR introduce _any_ user-facing change?
    
    None
    
    ### How was this patch tested?
    
    Existing IT test in
    
org.apache.gravitino.client.integration.test.authorization.TableAuthorizationIT
---
 .../java/org/apache/gravitino/EntityStore.java     | 31 ++++++++++++++++++++++
 .../gravitino/storage/relational/JDBCBackend.java  | 12 +++++++++
 .../storage/relational/RelationalBackend.java      | 12 +++++++++
 .../storage/relational/RelationalEntityStore.java  | 22 +++++++++++++++
 .../storage/relational/mapper/TableMetaMapper.java |  4 +++
 .../mapper/TableMetaSQLProviderFactory.java        |  5 ++++
 .../provider/base/TableMetaBaseSQLProvider.java    | 24 +++++++++++++++++
 .../relational/service/TableMetaService.java       | 29 ++++++++++++++++++++
 .../storage/memory/TestMemoryEntityStore.java      |  6 +++++
 .../server/authorization/MetadataAuthzHelper.java  | 20 +++++++++++++-
 10 files changed, 164 insertions(+), 1 deletion(-)

diff --git a/core/src/main/java/org/apache/gravitino/EntityStore.java 
b/core/src/main/java/org/apache/gravitino/EntityStore.java
index c2575dc9eb..d2c19c65c6 100644
--- a/core/src/main/java/org/apache/gravitino/EntityStore.java
+++ b/core/src/main/java/org/apache/gravitino/EntityStore.java
@@ -20,6 +20,8 @@ package org.apache.gravitino;
 
 import java.io.Closeable;
 import java.io.IOException;
+import java.lang.reflect.Array;
+import java.util.Arrays;
 import java.util.List;
 import java.util.function.Function;
 import org.apache.commons.lang3.tuple.Pair;
@@ -165,6 +167,35 @@ public interface EntityStore extends Closeable {
   <E extends Entity & HasIdentifier> E get(NameIdentifier ident, EntityType 
entityType, Class<E> e)
       throws NoSuchEntityException, IOException;
 
+  /**
+   * Batch get the entity from the underlying storage.
+   *
+   * @param idents the unique identifier of the entity
+   * @param entityType the general type of the entity
+   * @param clazz the entity class instance
+   * @param <E> the class of entity
+   * @return the entity retrieved from the underlying storage
+   * @throws NoSuchEntityException if the entity does not exist
+   */
+  <E extends Entity & HasIdentifier> List<E> batchGet(
+      List<NameIdentifier> idents, EntityType entityType, Class<E> clazz);
+
+  /**
+   * Batch get the entity from the underlying storage.
+   *
+   * @param idents the unique identifier of the entity
+   * @param entityType the general type of the entity
+   * @param clazz the entity class instance
+   * @param <E> the class of entity
+   * @return the entity retrieved from the underlying storage
+   * @throws NoSuchEntityException if the entity does not exist
+   */
+  default <E extends Entity & HasIdentifier> E[] batchGet(
+      NameIdentifier[] idents, EntityType entityType, Class<E> clazz) {
+    return batchGet(Arrays.asList(idents), entityType, clazz)
+        .toArray(size -> (E[]) Array.newInstance(clazz, size));
+  }
+
   /**
    * Delete the entity from the underlying storage by the specified {@link
    * org.apache.gravitino.NameIdentifier}.
diff --git 
a/core/src/main/java/org/apache/gravitino/storage/relational/JDBCBackend.java 
b/core/src/main/java/org/apache/gravitino/storage/relational/JDBCBackend.java
index f660653067..47c3a39f59 100644
--- 
a/core/src/main/java/org/apache/gravitino/storage/relational/JDBCBackend.java
+++ 
b/core/src/main/java/org/apache/gravitino/storage/relational/JDBCBackend.java
@@ -286,6 +286,18 @@ public class JDBCBackend implements RelationalBackend {
     }
   }
 
+  @Override
+  public <E extends Entity & HasIdentifier> List<E> batchGet(
+      List<NameIdentifier> identifiers, Entity.EntityType entityType) {
+    switch (entityType) {
+      case TABLE:
+        return (List<E>) 
TableMetaService.getInstance().batchGetTableByIdentifier(identifiers);
+      default:
+        throw new UnsupportedEntityTypeException(
+            "Unsupported entity type: %s for get operation", entityType);
+    }
+  }
+
   @Override
   public boolean delete(NameIdentifier ident, Entity.EntityType entityType, 
boolean cascade)
       throws IOException {
diff --git 
a/core/src/main/java/org/apache/gravitino/storage/relational/RelationalBackend.java
 
b/core/src/main/java/org/apache/gravitino/storage/relational/RelationalBackend.java
index 037d5256e0..61eb9c435b 100644
--- 
a/core/src/main/java/org/apache/gravitino/storage/relational/RelationalBackend.java
+++ 
b/core/src/main/java/org/apache/gravitino/storage/relational/RelationalBackend.java
@@ -113,6 +113,18 @@ public interface RelationalBackend extends Closeable, 
SupportsRelationOperations
   <E extends Entity & HasIdentifier> E get(NameIdentifier ident, 
Entity.EntityType entityType)
       throws IOException;
 
+  /**
+   * Batch retrieves the entities associated with the identifiers and the 
entity type.
+   *
+   * @param <E> The type of the entity returned.
+   * @param identifiers The identifiers of the entities.
+   * @param entityType The type of the entity.
+   * @return The entities associated with the identifiers and the entity type, 
or null if the key
+   *     does not exist.
+   */
+  <E extends Entity & HasIdentifier> List<E> batchGet(
+      List<NameIdentifier> identifiers, Entity.EntityType entityType);
+
   /**
    * Soft deletes the entity associated with the identifier and the entity 
type.
    *
diff --git 
a/core/src/main/java/org/apache/gravitino/storage/relational/RelationalEntityStore.java
 
b/core/src/main/java/org/apache/gravitino/storage/relational/RelationalEntityStore.java
index b4e8230e36..31183f2649 100644
--- 
a/core/src/main/java/org/apache/gravitino/storage/relational/RelationalEntityStore.java
+++ 
b/core/src/main/java/org/apache/gravitino/storage/relational/RelationalEntityStore.java
@@ -23,6 +23,7 @@ import static 
org.apache.gravitino.Configs.ENTITY_RELATIONAL_STORE;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.ImmutableMap;
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Optional;
 import java.util.function.Function;
@@ -153,6 +154,27 @@ public class RelationalEntityStore implements EntityStore, 
SupportsRelationOpera
         });
   }
 
+  @Override
+  public <E extends Entity & HasIdentifier> List<E> batchGet(
+      List<NameIdentifier> idents, Entity.EntityType entityType, Class<E> 
clazz) {
+    List<E> allEntities = new ArrayList<>();
+    List<NameIdentifier> noCacheIdents =
+        idents.stream()
+            .filter(
+                ident -> {
+                  Optional<E> entity = cache.getIfPresent(ident, entityType);
+                  entity.ifPresent(allEntities::add);
+                  return entity.isEmpty();
+                })
+            .toList();
+    List<E> fetchEntities = backend.batchGet(noCacheIdents, entityType);
+    for (E entity : fetchEntities) {
+      cache.put(entity);
+      allEntities.add(entity);
+    }
+    return allEntities;
+  }
+
   @Override
   public boolean delete(NameIdentifier ident, Entity.EntityType entityType, 
boolean cascade)
       throws IOException {
diff --git 
a/core/src/main/java/org/apache/gravitino/storage/relational/mapper/TableMetaMapper.java
 
b/core/src/main/java/org/apache/gravitino/storage/relational/mapper/TableMetaMapper.java
index c284d0ce3c..786df8b5f1 100644
--- 
a/core/src/main/java/org/apache/gravitino/storage/relational/mapper/TableMetaMapper.java
+++ 
b/core/src/main/java/org/apache/gravitino/storage/relational/mapper/TableMetaMapper.java
@@ -112,4 +112,8 @@ public interface TableMetaMapper {
       method = "deleteTableMetasByLegacyTimeline")
   Integer deleteTableMetasByLegacyTimeline(
       @Param("legacyTimeline") Long legacyTimeline, @Param("limit") int limit);
+
+  @SelectProvider(type = TableMetaSQLProviderFactory.class, method = 
"batchSelectTableByIdentifier")
+  List<TablePO> batchSelectTableByIdentifier(
+      @Param("schemaId") Long schemaId, @Param("tableNames") List<String> 
tableNames);
 }
diff --git 
a/core/src/main/java/org/apache/gravitino/storage/relational/mapper/TableMetaSQLProviderFactory.java
 
b/core/src/main/java/org/apache/gravitino/storage/relational/mapper/TableMetaSQLProviderFactory.java
index 2a796a6e15..20859f18d7 100644
--- 
a/core/src/main/java/org/apache/gravitino/storage/relational/mapper/TableMetaSQLProviderFactory.java
+++ 
b/core/src/main/java/org/apache/gravitino/storage/relational/mapper/TableMetaSQLProviderFactory.java
@@ -124,4 +124,9 @@ public class TableMetaSQLProviderFactory {
       @Param("legacyTimeline") Long legacyTimeline, @Param("limit") int limit) 
{
     return getProvider().deleteTableMetasByLegacyTimeline(legacyTimeline, 
limit);
   }
+
+  public static String batchSelectTableByIdentifier(
+      @Param("schemaId") Long schemaId, @Param("tableNames") List<String> 
tableNames) {
+    return getProvider().batchSelectTableByIdentifier(schemaId, tableNames);
+  }
 }
diff --git 
a/core/src/main/java/org/apache/gravitino/storage/relational/mapper/provider/base/TableMetaBaseSQLProvider.java
 
b/core/src/main/java/org/apache/gravitino/storage/relational/mapper/provider/base/TableMetaBaseSQLProvider.java
index 4312091fca..4316f87561 100644
--- 
a/core/src/main/java/org/apache/gravitino/storage/relational/mapper/provider/base/TableMetaBaseSQLProvider.java
+++ 
b/core/src/main/java/org/apache/gravitino/storage/relational/mapper/provider/base/TableMetaBaseSQLProvider.java
@@ -337,4 +337,28 @@ public class TableMetaBaseSQLProvider {
             TABLE_NAME,
             TableVersionMapper.TABLE_NAME);
   }
+
+  public String batchSelectTableByIdentifier(
+      @Param("schemaId") Long schemaId, @Param("tableNames") List<String> 
tableNames) {
+    return """
+            <script>
+            SELECT
+            tm.table_id AS tableId,
+            tm.table_name AS tableName,
+            tm.metalake_id AS metalakeId,
+            tm.audit_info AS auditInfo,
+            tm.current_version AS currentVersion,
+            tm.last_version AS lastVersion,
+            tm.deleted_at AS deletedAt
+            FROM %s tm
+            WHERE schema_id = #{schemaId}
+            AND table_name IN
+            <foreach collection="tableNames" item="tableName" open="(" 
separator="," close=")">
+            #{tableName}
+            </foreach>
+             AND deleted_at = 0
+             </script>
+            """
+        .formatted(TABLE_NAME);
+  }
 }
diff --git 
a/core/src/main/java/org/apache/gravitino/storage/relational/service/TableMetaService.java
 
b/core/src/main/java/org/apache/gravitino/storage/relational/service/TableMetaService.java
index 51653f2792..7bd7cdd56a 100644
--- 
a/core/src/main/java/org/apache/gravitino/storage/relational/service/TableMetaService.java
+++ 
b/core/src/main/java/org/apache/gravitino/storage/relational/service/TableMetaService.java
@@ -22,12 +22,15 @@ import static 
org.apache.gravitino.metrics.source.MetricsSource.GRAVITINO_RELATI
 
 import com.google.common.base.Preconditions;
 import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.function.Function;
 import java.util.stream.Collectors;
+import org.apache.commons.collections4.CollectionUtils;
 import org.apache.gravitino.Entity;
 import org.apache.gravitino.Entity.EntityType;
 import org.apache.gravitino.GravitinoEnv;
@@ -290,6 +293,32 @@ public class TableMetaService {
         mapper -> mapper.deleteTableVersionByLegacyTimeline(legacyTimeline, 
limit));
   }
 
+  @Monitored(
+      metricsSource = GRAVITINO_RELATIONAL_STORE_METRIC_NAME,
+      baseMetricName = "batchGetTableByIdentifier")
+  public List<TableEntity> batchGetTableByIdentifier(List<NameIdentifier> 
identifiers) {
+    if (CollectionUtils.isEmpty(identifiers)) {
+      return Collections.emptyList();
+    }
+    NameIdentifier firstIdent = identifiers.get(0);
+    NameIdentifier schemaIdent = 
NameIdentifierUtil.getSchemaIdentifier(firstIdent);
+    List<String> tableNames = new ArrayList<>(identifiers.size());
+    tableNames.add(identifiers.get(0).name());
+    for (int i = 1; i < identifiers.size(); i++) {
+      NameIdentifier ident = identifiers.get(i);
+      Preconditions.checkArgument(
+          Objects.equals(schemaIdent, 
NameIdentifierUtil.getSchemaIdentifier(ident)));
+      tableNames.add(ident.name());
+    }
+    Long schemaId = EntityIdService.getEntityId(schemaIdent, 
Entity.EntityType.SCHEMA);
+    return SessionUtils.doWithCommitAndFetchResult(
+        TableMetaMapper.class,
+        mapper -> {
+          List<TablePO> tableList = 
mapper.batchSelectTableByIdentifier(schemaId, tableNames);
+          return POConverters.fromTablePOs(tableList, firstIdent.namespace());
+        });
+  }
+
   private void fillTablePOBuilderParentEntityId(TablePO.Builder builder, 
Namespace namespace) {
     NamespaceUtil.checkTable(namespace);
     NamespacedEntityId namespacedEntityId =
diff --git 
a/core/src/test/java/org/apache/gravitino/storage/memory/TestMemoryEntityStore.java
 
b/core/src/test/java/org/apache/gravitino/storage/memory/TestMemoryEntityStore.java
index 95dded4f9c..4c5bc8043e 100644
--- 
a/core/src/test/java/org/apache/gravitino/storage/memory/TestMemoryEntityStore.java
+++ 
b/core/src/test/java/org/apache/gravitino/storage/memory/TestMemoryEntityStore.java
@@ -146,6 +146,12 @@ public class TestMemoryEntityStore {
       return e;
     }
 
+    @Override
+    public <E extends Entity & HasIdentifier> List<E> batchGet(
+        List<NameIdentifier> idents, EntityType entityType, Class<E> e) {
+      return idents.stream().map(ident -> (E) entityMap.get(ident)).toList();
+    }
+
     @Override
     public boolean delete(NameIdentifier ident, EntityType entityType, boolean 
cascade)
         throws IOException {
diff --git 
a/server-common/src/main/java/org/apache/gravitino/server/authorization/MetadataAuthzHelper.java
 
b/server-common/src/main/java/org/apache/gravitino/server/authorization/MetadataAuthzHelper.java
index 028747559b..451a205c4b 100644
--- 
a/server-common/src/main/java/org/apache/gravitino/server/authorization/MetadataAuthzHelper.java
+++ 
b/server-common/src/main/java/org/apache/gravitino/server/authorization/MetadataAuthzHelper.java
@@ -38,6 +38,7 @@ import org.apache.gravitino.NameIdentifier;
 import org.apache.gravitino.authorization.AuthorizationRequestContext;
 import org.apache.gravitino.authorization.GravitinoAuthorizer;
 import org.apache.gravitino.dto.tag.MetadataObjectDTO;
+import org.apache.gravitino.meta.TableEntity;
 import 
org.apache.gravitino.server.authorization.expression.AuthorizationExpressionConstants;
 import 
org.apache.gravitino.server.authorization.expression.AuthorizationExpressionEvaluator;
 import org.apache.gravitino.utils.MetadataObjectUtil;
@@ -136,6 +137,7 @@ public class MetadataAuthzHelper {
       String expression,
       Entity.EntityType entityType,
       NameIdentifier[] nameIdentifiers) {
+    preloadToCache(entityType, nameIdentifiers);
     return filterByExpression(metalake, expression, entityType, 
nameIdentifiers, e -> e);
   }
 
@@ -291,7 +293,12 @@ public class MetadataAuthzHelper {
     return futures.stream()
         .map(CompletableFuture::join)
         .filter(Objects::nonNull)
-        .toArray(size -> (E[]) 
Array.newInstance(entities.getClass().getComponentType(), size));
+        .toArray(size -> createArray(entities.getClass().getComponentType(), 
size));
+  }
+
+  @SuppressWarnings("unchecked")
+  private static <E> E[] createArray(Class<?> componentType, int size) {
+    return (E[]) Array.newInstance(componentType, size);
   }
 
   private static boolean enableAuthorization() {
@@ -318,4 +325,15 @@ public class MetadataAuthzHelper {
       }
     }
   }
+
+  private static void preloadToCache(
+      Entity.EntityType entityType, NameIdentifier[] nameIdentifiers) {
+    if (GravitinoEnv.getInstance().cacheEnabled()) {
+      if (entityType == Entity.EntityType.TABLE) {
+        GravitinoEnv.getInstance()
+            .entityStore()
+            .batchGet(nameIdentifiers, entityType, TableEntity.class);
+      }
+    }
+  }
 }

Reply via email to