This is an automated email from the ASF dual-hosted git repository.
jshao 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 9f46fd73ac [#9204] fix(core): Fix can't clear cache when the user of a
metadata object is deleted. (#9205)
9f46fd73ac is described below
commit 9f46fd73acc3e88d98a385f6f14ad2b2b6ed5d6e
Author: Mini Yu <[email protected]>
AuthorDate: Wed Nov 26 14:14:47 2025 +0800
[#9204] fix(core): Fix can't clear cache when the user of a metadata object
is deleted. (#9205)
### What changes were proposed in this pull request?
Add the reverse index for owner and metadata objects.
### Why are the changes needed?
Without this, we will get an out-of-date user information.
Fix: #9204
### Does this PR introduce _any_ user-facing change?
N/A
### How was this patch tested?
ITs
---
.../integration/test/authorization/OwnerIT.java | 47 ++++++++++++++++++++++
.../apache/gravitino/cache/ReverseIndexCache.java | 30 +++++++++-----
.../apache/gravitino/cache/ReverseIndexRules.java | 33 ++++++++++++---
3 files changed, 93 insertions(+), 17 deletions(-)
diff --git
a/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/authorization/OwnerIT.java
b/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/authorization/OwnerIT.java
index 280489f6db..0f59d46c8b 100644
---
a/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/authorization/OwnerIT.java
+++
b/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/authorization/OwnerIT.java
@@ -32,6 +32,7 @@ import org.apache.gravitino.authorization.Owner;
import org.apache.gravitino.authorization.Privileges;
import org.apache.gravitino.authorization.SecurableObject;
import org.apache.gravitino.authorization.SecurableObjects;
+import org.apache.gravitino.client.GravitinoClient;
import org.apache.gravitino.client.GravitinoMetalake;
import org.apache.gravitino.exceptions.NoSuchMetadataObjectException;
import org.apache.gravitino.exceptions.NotFoundException;
@@ -413,4 +414,50 @@ public class OwnerIT extends BaseIT {
// Cleanup
client.dropMetalake(metalakeNameE, true);
}
+
+ @Test
+ void testOwnerDrop() {
+ String metalakeNameE = RandomNameUtils.genRandomName("metalakeE");
+ String catalogNameE = RandomNameUtils.genRandomName("catalogE");
+ GravitinoMetalake metalake =
+ client.createMetalake(metalakeNameE, "metalake E comment",
Collections.emptyMap());
+ metalake.createCatalog(
+ catalogNameE, Catalog.Type.FILESET, "hadoop", "comment",
Collections.emptyMap());
+ // Create a user
+ String userName = "ownerUser";
+ metalake.addUser(userName);
+
+ // Now set the owner of catalog to the user
+ MetadataObject catalogObject =
+ MetadataObjects.of(Lists.newArrayList(catalogNameE),
MetadataObject.Type.CATALOG);
+ metalake.setOwner(catalogObject, userName, Owner.Type.USER);
+ Owner owner = metalake.getOwner(catalogObject).get();
+ Assertions.assertEquals(userName, owner.name());
+
+ try (GravitinoClient ordinaryClient =
+
GravitinoClient.builder(serverUri).withMetalake(metalakeNameE).build()) {
+ // Drop the user
+ ordinaryClient.removeUser(userName);
+ // The owner should be removed as well
+ Owner ownerAfterDrop = metalake.getOwner(catalogObject).orElse(null);
+ Assertions.assertNull(ownerAfterDrop);
+
+ // create a Group
+ String groupName = "ownerGroup";
+ metalake.addGroup(groupName);
+ // Now set the owner of catalog to the group
+ metalake.setOwner(catalogObject, groupName, Owner.Type.GROUP);
+ owner = metalake.getOwner(catalogObject).get();
+ Assertions.assertEquals(groupName, owner.name());
+
+ // Drop the group
+ ordinaryClient.removeGroup(groupName);
+ // The owner should be removed as well
+ ownerAfterDrop = metalake.getOwner(catalogObject).orElse(null);
+ Assertions.assertNull(ownerAfterDrop);
+ }
+ // Cleanup
+ client.disableMetalake(metalakeNameE);
+ client.dropMetalake(metalakeNameE, true);
+ }
}
diff --git
a/core/src/main/java/org/apache/gravitino/cache/ReverseIndexCache.java
b/core/src/main/java/org/apache/gravitino/cache/ReverseIndexCache.java
index 16870d0411..49cc91e8b8 100644
--- a/core/src/main/java/org/apache/gravitino/cache/ReverseIndexCache.java
+++ b/core/src/main/java/org/apache/gravitino/cache/ReverseIndexCache.java
@@ -19,12 +19,14 @@
package org.apache.gravitino.cache;
import com.google.common.base.Preconditions;
+import com.google.common.collect.HashMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
+import com.google.common.collect.Multimap;
import com.googlecode.concurrenttrees.radix.ConcurrentRadixTree;
import com.googlecode.concurrenttrees.radix.RadixTree;
import
com.googlecode.concurrenttrees.radix.node.concrete.DefaultCharArrayNodeFactory;
-import java.util.HashMap;
+import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.apache.commons.collections4.CollectionUtils;
@@ -43,9 +45,11 @@ import org.apache.gravitino.meta.UserEntity;
* efficiently store and retrieve relationships between entities based on
their keys.
*/
public class ReverseIndexCache {
- private RadixTree<List<EntityCacheKey>> reverseIndex;
+ private final RadixTree<List<EntityCacheKey>> reverseIndex;
+
/** Registers a reverse index processor for a specific entity class. */
- private final Map<Class<? extends Entity>, ReverseIndexRule>
reverseIndexRules = new HashMap<>();
+ private final Multimap<Class<? extends Entity>, ReverseIndexRule>
reverseIndexRules =
+ HashMultimap.create();
/**
* Map from data entity key to a list of entity cache relation keys. This is
used for reverse
@@ -73,11 +77,13 @@ public class ReverseIndexCache {
public ReverseIndexCache() {
this.reverseIndex = new ConcurrentRadixTree<>(new
DefaultCharArrayNodeFactory());
- registerReverseRule(UserEntity.class, ReverseIndexRules.USER_REVERSE_RULE);
- registerReverseRule(GroupEntity.class,
ReverseIndexRules.GROUP_REVERSE_RULE);
- registerReverseRule(RoleEntity.class, ReverseIndexRules.ROLE_REVERSE_RULE);
- registerReverseRule(PolicyEntity.class,
ReverseIndexRules.POLICY_REVERSE_RULE);
- registerReverseRule(TagEntity.class, ReverseIndexRules.TAG_REVERSE_RULE);
+ registerReverseRule(UserEntity.class,
ReverseIndexRules.USER_ROLE_REVERSE_RULE);
+ registerReverseRule(UserEntity.class,
ReverseIndexRules.USER_OWNERSHIP_REVERSE_RULE);
+ registerReverseRule(GroupEntity.class,
ReverseIndexRules.GROUP_ROLE_REVERSE_RULE);
+ registerReverseRule(GroupEntity.class,
ReverseIndexRules.GROUP_OWNERSHIP_REVERSE_RULE);
+ registerReverseRule(RoleEntity.class,
ReverseIndexRules.ROLE_SECURABLE_OBJECT_REVERSE_RULE);
+ registerReverseRule(PolicyEntity.class,
ReverseIndexRules.POLICY_SECURABLE_OBJECT_REVERSE_RULE);
+ registerReverseRule(TagEntity.class,
ReverseIndexRules.TAG_SECURABLE_OBJECT_REVERSE_RULE);
registerReverseRule(
GenericEntity.class,
ReverseIndexRules.GENERIC_METADATA_OBJECT_REVERSE_RULE);
}
@@ -149,9 +155,11 @@ public class ReverseIndexCache {
/** Processes an entity and updates the reverse index accordingly. */
public void indexEntity(Entity entity, EntityCacheRelationKey key) {
- ReverseIndexRule rule = reverseIndexRules.get(entity.getClass());
- if (rule != null) {
- rule.indexEntity(entity, key, this);
+ Collection<ReverseIndexRule> rules =
reverseIndexRules.get(entity.getClass());
+ if (!rules.isEmpty()) {
+ for (ReverseIndexRule rule : rules) {
+ rule.indexEntity(entity, key, this);
+ }
}
}
diff --git
a/core/src/main/java/org/apache/gravitino/cache/ReverseIndexRules.java
b/core/src/main/java/org/apache/gravitino/cache/ReverseIndexRules.java
index f568320860..ca7ad074a4 100644
--- a/core/src/main/java/org/apache/gravitino/cache/ReverseIndexRules.java
+++ b/core/src/main/java/org/apache/gravitino/cache/ReverseIndexRules.java
@@ -23,6 +23,7 @@ import org.apache.gravitino.Entity;
import org.apache.gravitino.Entity.EntityType;
import org.apache.gravitino.NameIdentifier;
import org.apache.gravitino.Namespace;
+import org.apache.gravitino.SupportsRelationOperations;
import org.apache.gravitino.meta.GenericEntity;
import org.apache.gravitino.meta.GroupEntity;
import org.apache.gravitino.meta.PolicyEntity;
@@ -42,7 +43,7 @@ import org.apache.gravitino.utils.NamespaceUtil;
public class ReverseIndexRules {
/** UserEntity reverse index processor */
- public static final ReverseIndexCache.ReverseIndexRule USER_REVERSE_RULE =
+ public static final ReverseIndexCache.ReverseIndexRule
USER_ROLE_REVERSE_RULE =
(entity, key, reverseIndexCache) -> {
UserEntity userEntity = (UserEntity) entity;
if (userEntity.roleNames() != null) {
@@ -57,8 +58,18 @@ public class ReverseIndexRules {
}
};
+ public static final ReverseIndexCache.ReverseIndexRule
USER_OWNERSHIP_REVERSE_RULE =
+ (entity, key, reverseIndexCache) -> {
+ UserEntity userEntity = (UserEntity) entity;
+ // Handle Securable Objects -> User reverse index, so the key type is
User and the value
+ // type is securable Object.
+ if (key.relationType() == SupportsRelationOperations.Type.OWNER_REL) {
+ reverseIndexCache.put(userEntity.nameIdentifier(), EntityType.USER,
key);
+ }
+ };
+
/** GroupEntity reverse index processor */
- public static final ReverseIndexCache.ReverseIndexRule GROUP_REVERSE_RULE =
+ public static final ReverseIndexCache.ReverseIndexRule
GROUP_ROLE_REVERSE_RULE =
(entity, key, reverseIndexCache) -> {
GroupEntity groupEntity = (GroupEntity) entity;
if (groupEntity.roleNames() != null) {
@@ -73,8 +84,18 @@ public class ReverseIndexRules {
}
};
- /** * RoleEntity reverse index processor */
- public static final ReverseIndexCache.ReverseIndexRule ROLE_REVERSE_RULE =
+ public static final ReverseIndexCache.ReverseIndexRule
GROUP_OWNERSHIP_REVERSE_RULE =
+ (entity, key, reverseIndexCache) -> {
+ GroupEntity groupEntity = (GroupEntity) entity;
+ // Handle Securable Objects -> Group reverse index, so the key type is
group and the value
+ // type is securable Object.
+ if (key.relationType() == SupportsRelationOperations.Type.OWNER_REL) {
+ reverseIndexCache.put(groupEntity.nameIdentifier(),
EntityType.GROUP, key);
+ }
+ };
+
+ /** RoleEntity reverse index processor */
+ public static final ReverseIndexCache.ReverseIndexRule
ROLE_SECURABLE_OBJECT_REVERSE_RULE =
(entity, key, reverseIndexCache) -> {
RoleEntity roleEntity = (RoleEntity) entity;
if (roleEntity.securableObjects() != null) {
@@ -166,7 +187,7 @@ public class ReverseIndexRules {
// Keep objects to policies reverse index for policy objects, so the key are
policies and the
// values are objects.
- public static final ReverseIndexCache.ReverseIndexRule POLICY_REVERSE_RULE =
+ public static final ReverseIndexCache.ReverseIndexRule
POLICY_SECURABLE_OBJECT_REVERSE_RULE =
(entity, key, reverseIndexCache) -> {
PolicyEntity policyEntity = (PolicyEntity) entity;
NameIdentifier nameIdentifier =
@@ -176,7 +197,7 @@ public class ReverseIndexRules {
// Keep objects to tags reverse index for tag objects, so the key are tags
and the
// values are objects.
- public static final ReverseIndexCache.ReverseIndexRule TAG_REVERSE_RULE =
+ public static final ReverseIndexCache.ReverseIndexRule
TAG_SECURABLE_OBJECT_REVERSE_RULE =
(entity, key, reverseIndexCache) -> {
TagEntity tagEntity = (TagEntity) entity;
NameIdentifier nameIdentifier =
NameIdentifier.of(tagEntity.namespace(), tagEntity.name());