Skip to content

Commit 9e0ab93

Browse files
github-actions[bot]hdygxsjroryqi
authored
[#9802] feat(authz): support preload table metadata in batch metadata authorization (#9831)
### 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 --------- Co-authored-by: yangyang zhong <35210666+hdygxsj@users.noreply.github.com> Co-authored-by: Rory <he@datastrato.com>
1 parent 975faa1 commit 9e0ab93

File tree

11 files changed

+222
-1
lines changed

11 files changed

+222
-1
lines changed

core/src/main/java/org/apache/gravitino/EntityStore.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020

2121
import java.io.Closeable;
2222
import java.io.IOException;
23+
import java.lang.reflect.Array;
24+
import java.util.Arrays;
2325
import java.util.List;
2426
import java.util.function.Function;
2527
import org.apache.commons.lang3.tuple.Pair;
@@ -165,6 +167,35 @@ <E extends Entity & HasIdentifier> E update(
165167
<E extends Entity & HasIdentifier> E get(NameIdentifier ident, EntityType entityType, Class<E> e)
166168
throws NoSuchEntityException, IOException;
167169

170+
/**
171+
* Batch get the entity from the underlying storage.
172+
*
173+
* @param idents the unique identifier of the entity
174+
* @param entityType the general type of the entity
175+
* @param clazz the entity class instance
176+
* @param <E> the class of entity
177+
* @return the entity retrieved from the underlying storage
178+
* @throws NoSuchEntityException if the entity does not exist
179+
*/
180+
<E extends Entity & HasIdentifier> List<E> batchGet(
181+
List<NameIdentifier> idents, EntityType entityType, Class<E> clazz);
182+
183+
/**
184+
* Batch get the entity from the underlying storage.
185+
*
186+
* @param idents the unique identifier of the entity
187+
* @param entityType the general type of the entity
188+
* @param clazz the entity class instance
189+
* @param <E> the class of entity
190+
* @return the entity retrieved from the underlying storage
191+
* @throws NoSuchEntityException if the entity does not exist
192+
*/
193+
default <E extends Entity & HasIdentifier> E[] batchGet(
194+
NameIdentifier[] idents, EntityType entityType, Class<E> clazz) {
195+
return batchGet(Arrays.asList(idents), entityType, clazz)
196+
.toArray(size -> (E[]) Array.newInstance(clazz, size));
197+
}
198+
168199
/**
169200
* Delete the entity from the underlying storage by the specified {@link
170201
* org.apache.gravitino.NameIdentifier}.

core/src/main/java/org/apache/gravitino/storage/relational/JDBCBackend.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,18 @@ public <E extends Entity & HasIdentifier> E get(
286286
}
287287
}
288288

289+
@Override
290+
public <E extends Entity & HasIdentifier> List<E> batchGet(
291+
List<NameIdentifier> identifiers, Entity.EntityType entityType) {
292+
switch (entityType) {
293+
case TABLE:
294+
return (List<E>) TableMetaService.getInstance().batchGetTableByIdentifier(identifiers);
295+
default:
296+
throw new UnsupportedEntityTypeException(
297+
"Unsupported entity type: %s for get operation", entityType);
298+
}
299+
}
300+
289301
@Override
290302
public boolean delete(NameIdentifier ident, Entity.EntityType entityType, boolean cascade)
291303
throws IOException {

core/src/main/java/org/apache/gravitino/storage/relational/RelationalBackend.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,18 @@ <E extends Entity & HasIdentifier> E update(
113113
<E extends Entity & HasIdentifier> E get(NameIdentifier ident, Entity.EntityType entityType)
114114
throws IOException;
115115

116+
/**
117+
* Batch retrieves the entities associated with the identifiers and the entity type.
118+
*
119+
* @param <E> The type of the entity returned.
120+
* @param identifiers The identifiers of the entities.
121+
* @param entityType The type of the entity.
122+
* @return The entities associated with the identifiers and the entity type, or null if the key
123+
* does not exist.
124+
*/
125+
<E extends Entity & HasIdentifier> List<E> batchGet(
126+
List<NameIdentifier> identifiers, Entity.EntityType entityType);
127+
116128
/**
117129
* Soft deletes the entity associated with the identifier and the entity type.
118130
*

core/src/main/java/org/apache/gravitino/storage/relational/RelationalEntityStore.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import com.google.common.annotations.VisibleForTesting;
2424
import com.google.common.collect.ImmutableMap;
2525
import java.io.IOException;
26+
import java.util.ArrayList;
2627
import java.util.List;
2728
import java.util.Optional;
2829
import java.util.function.Function;
@@ -153,6 +154,27 @@ public <E extends Entity & HasIdentifier> E get(
153154
});
154155
}
155156

157+
@Override
158+
public <E extends Entity & HasIdentifier> List<E> batchGet(
159+
List<NameIdentifier> idents, Entity.EntityType entityType, Class<E> clazz) {
160+
List<E> allEntities = new ArrayList<>();
161+
List<NameIdentifier> noCacheIdents =
162+
idents.stream()
163+
.filter(
164+
ident -> {
165+
Optional<E> entity = cache.getIfPresent(ident, entityType);
166+
entity.ifPresent(allEntities::add);
167+
return entity.isEmpty();
168+
})
169+
.toList();
170+
List<E> fetchEntities = backend.batchGet(noCacheIdents, entityType);
171+
for (E entity : fetchEntities) {
172+
cache.put(entity);
173+
allEntities.add(entity);
174+
}
175+
return allEntities;
176+
}
177+
156178
@Override
157179
public boolean delete(NameIdentifier ident, Entity.EntityType entityType, boolean cascade)
158180
throws IOException {

core/src/main/java/org/apache/gravitino/storage/relational/mapper/TableMetaMapper.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,4 +95,8 @@ Integer updateTableMeta(
9595
method = "deleteTableMetasByLegacyTimeline")
9696
Integer deleteTableMetasByLegacyTimeline(
9797
@Param("legacyTimeline") Long legacyTimeline, @Param("limit") int limit);
98+
99+
@SelectProvider(type = TableMetaSQLProviderFactory.class, method = "batchSelectTableByIdentifier")
100+
List<TablePO> batchSelectTableByIdentifier(
101+
@Param("schemaId") Long schemaId, @Param("tableNames") List<String> tableNames);
98102
}

core/src/main/java/org/apache/gravitino/storage/relational/mapper/TableMetaSQLProviderFactory.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,4 +108,9 @@ public static String deleteTableMetasByLegacyTimeline(
108108
@Param("legacyTimeline") Long legacyTimeline, @Param("limit") int limit) {
109109
return getProvider().deleteTableMetasByLegacyTimeline(legacyTimeline, limit);
110110
}
111+
112+
public static String batchSelectTableByIdentifier(
113+
@Param("schemaId") Long schemaId, @Param("tableNames") List<String> tableNames) {
114+
return getProvider().batchSelectTableByIdentifier(schemaId, tableNames);
115+
}
111116
}

core/src/main/java/org/apache/gravitino/storage/relational/mapper/provider/base/TableMetaBaseSQLProvider.java

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
import static org.apache.gravitino.storage.relational.mapper.TableMetaMapper.TABLE_NAME;
2222

2323
import java.util.List;
24+
import org.apache.gravitino.storage.relational.mapper.CatalogMetaMapper;
25+
import org.apache.gravitino.storage.relational.mapper.MetalakeMetaMapper;
26+
import org.apache.gravitino.storage.relational.mapper.SchemaMetaMapper;
2427
import org.apache.gravitino.storage.relational.mapper.TableVersionMapper;
2528
import org.apache.gravitino.storage.relational.po.TablePO;
2629
import org.apache.ibatis.annotations.Param;
@@ -230,4 +233,81 @@ public String deleteTableMetasByLegacyTimeline(
230233
+ TABLE_NAME
231234
+ " WHERE deleted_at > 0 AND deleted_at < #{legacyTimeline} LIMIT #{limit}";
232235
}
236+
237+
public String selectTableByFullQualifiedName(
238+
@Param("metalakeName") String metalakeName,
239+
@Param("catalogName") String catalogName,
240+
@Param("schemaName") String schemaName,
241+
@Param("tableName") String tableName) {
242+
return """
243+
SELECT
244+
sm.schema_id AS schemaId,
245+
cm.catalog_id AS catalogId,
246+
tm.table_id AS tableId,
247+
tm.table_name AS tableName,
248+
tm.metalake_id AS metalakeId,
249+
tm.audit_info AS auditInfo,
250+
tm.current_version AS currentVersion,
251+
tm.last_version AS lastVersion,
252+
tm.deleted_at AS deletedAt,
253+
tvi.format AS format,
254+
tvi.properties AS properties,
255+
tvi.partitioning AS partitions,
256+
tvi.sort_orders AS sortOrders,
257+
tvi.distribution AS distribution,
258+
tvi.indexes AS indexes,
259+
tvi.comment AS comment
260+
FROM
261+
%s mm
262+
INNER JOIN
263+
%s cm ON mm.metalake_id = cm.metalake_id
264+
AND cm.catalog_name = #{catalogName}
265+
AND cm.deleted_at = 0
266+
LEFT JOIN
267+
%s sm ON cm.catalog_id = sm.catalog_id
268+
AND sm.schema_name = #{schemaName}
269+
AND sm.deleted_at = 0
270+
LEFT JOIN
271+
%s tm ON sm.schema_id = tm.schema_id
272+
AND tm.table_name = #{tableName}
273+
AND tm.deleted_at = 0
274+
LEFT JOIN
275+
%s tvi ON tm.table_id = tvi.table_id
276+
AND tm.current_version = tvi.version
277+
AND tvi.deleted_at = 0
278+
WHERE
279+
mm.metalake_name = #{metalakeName}
280+
AND mm.deleted_at = 0;
281+
"""
282+
.formatted(
283+
MetalakeMetaMapper.TABLE_NAME,
284+
CatalogMetaMapper.TABLE_NAME,
285+
SchemaMetaMapper.TABLE_NAME,
286+
TABLE_NAME,
287+
TableVersionMapper.TABLE_NAME);
288+
}
289+
290+
public String batchSelectTableByIdentifier(
291+
@Param("schemaId") Long schemaId, @Param("tableNames") List<String> tableNames) {
292+
return """
293+
<script>
294+
SELECT
295+
tm.table_id AS tableId,
296+
tm.table_name AS tableName,
297+
tm.metalake_id AS metalakeId,
298+
tm.audit_info AS auditInfo,
299+
tm.current_version AS currentVersion,
300+
tm.last_version AS lastVersion,
301+
tm.deleted_at AS deletedAt
302+
FROM %s tm
303+
WHERE schema_id = #{schemaId}
304+
AND table_name IN
305+
<foreach collection="tableNames" item="tableName" open="(" separator="," close=")">
306+
#{tableName}
307+
</foreach>
308+
AND deleted_at = 0
309+
</script>
310+
"""
311+
.formatted(TABLE_NAME);
312+
}
233313
}

core/src/main/java/org/apache/gravitino/storage/relational/service/TableMetaService.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,14 @@
2222

2323
import com.google.common.base.Preconditions;
2424
import java.io.IOException;
25+
import java.util.ArrayList;
26+
import java.util.Collections;
2527
import java.util.List;
2628
import java.util.Objects;
2729
import java.util.concurrent.atomic.AtomicInteger;
2830
import java.util.concurrent.atomic.AtomicReference;
2931
import java.util.function.Function;
32+
import org.apache.commons.collections4.CollectionUtils;
3033
import org.apache.gravitino.Entity;
3134
import org.apache.gravitino.HasIdentifier;
3235
import org.apache.gravitino.MetadataObject;
@@ -319,6 +322,32 @@ public int deleteTableVersionByLegacyTimeline(Long legacyTimeline, int limit) {
319322
mapper -> mapper.deleteTableVersionByLegacyTimeline(legacyTimeline, limit));
320323
}
321324

325+
@Monitored(
326+
metricsSource = GRAVITINO_RELATIONAL_STORE_METRIC_NAME,
327+
baseMetricName = "batchGetTableByIdentifier")
328+
public List<TableEntity> batchGetTableByIdentifier(List<NameIdentifier> identifiers) {
329+
if (CollectionUtils.isEmpty(identifiers)) {
330+
return Collections.emptyList();
331+
}
332+
NameIdentifier firstIdent = identifiers.get(0);
333+
NameIdentifier schemaIdent = NameIdentifierUtil.getSchemaIdentifier(firstIdent);
334+
List<String> tableNames = new ArrayList<>(identifiers.size());
335+
tableNames.add(identifiers.get(0).name());
336+
for (int i = 1; i < identifiers.size(); i++) {
337+
NameIdentifier ident = identifiers.get(i);
338+
Preconditions.checkArgument(
339+
Objects.equals(schemaIdent, NameIdentifierUtil.getSchemaIdentifier(ident)));
340+
tableNames.add(ident.name());
341+
}
342+
Long schemaId = EntityIdService.getEntityId(schemaIdent, Entity.EntityType.SCHEMA);
343+
return SessionUtils.doWithCommitAndFetchResult(
344+
TableMetaMapper.class,
345+
mapper -> {
346+
List<TablePO> tableList = mapper.batchSelectTableByIdentifier(schemaId, tableNames);
347+
return POConverters.fromTablePOs(tableList, firstIdent.namespace());
348+
});
349+
}
350+
322351
private void fillTablePOBuilderParentEntityId(TablePO.Builder builder, Namespace namespace) {
323352
NamespaceUtil.checkTable(namespace);
324353
NamespacedEntityId namespacedEntityId =

core/src/test/java/org/apache/gravitino/storage/memory/TestMemoryEntityStore.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,12 @@ public <E extends Entity & HasIdentifier> E get(
145145
return e;
146146
}
147147

148+
@Override
149+
public <E extends Entity & HasIdentifier> List<E> batchGet(
150+
List<NameIdentifier> idents, EntityType entityType, Class<E> e) {
151+
return idents.stream().map(ident -> (E) entityMap.get(ident)).toList();
152+
}
153+
148154
@Override
149155
public boolean delete(NameIdentifier ident, EntityType entityType, boolean cascade)
150156
throws IOException {

server-common/src/main/java/org/apache/gravitino/server/authorization/MetadataAuthzHelper.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import org.apache.gravitino.authorization.AuthorizationRequestContext;
3939
import org.apache.gravitino.authorization.GravitinoAuthorizer;
4040
import org.apache.gravitino.dto.tag.MetadataObjectDTO;
41+
import org.apache.gravitino.meta.TableEntity;
4142
import org.apache.gravitino.server.authorization.expression.AuthorizationExpressionConstants;
4243
import org.apache.gravitino.server.authorization.expression.AuthorizationExpressionEvaluator;
4344
import org.apache.gravitino.utils.MetadataObjectUtil;
@@ -136,6 +137,7 @@ public static NameIdentifier[] filterByExpression(
136137
String expression,
137138
Entity.EntityType entityType,
138139
NameIdentifier[] nameIdentifiers) {
140+
preloadToCache(entityType, nameIdentifiers);
139141
return filterByExpression(metalake, expression, entityType, nameIdentifiers, e -> e);
140142
}
141143

@@ -291,7 +293,12 @@ private static <E> E[] doFilter(
291293
return futures.stream()
292294
.map(CompletableFuture::join)
293295
.filter(Objects::nonNull)
294-
.toArray(size -> (E[]) Array.newInstance(entities.getClass().getComponentType(), size));
296+
.toArray(size -> createArray(entities.getClass().getComponentType(), size));
297+
}
298+
299+
@SuppressWarnings("unchecked")
300+
private static <E> E[] createArray(Class<?> componentType, int size) {
301+
return (E[]) Array.newInstance(componentType, size);
295302
}
296303

297304
private static boolean enableAuthorization() {
@@ -318,4 +325,16 @@ private static void checkExecutor() {
318325
}
319326
}
320327
}
328+
329+
private static void preloadToCache(
330+
Entity.EntityType entityType, NameIdentifier[] nameIdentifiers) {
331+
Config config = GravitinoEnv.getInstance().config();
332+
if (config != null && !config.get(Configs.CACHE_ENABLED)) {
333+
if (entityType == Entity.EntityType.TABLE) {
334+
GravitinoEnv.getInstance()
335+
.entityStore()
336+
.batchGet(nameIdentifiers, entityType, TableEntity.class);
337+
}
338+
}
339+
}
321340
}

0 commit comments

Comments
 (0)