diqiu50 commented on code in PR #12006:
URL: https://github.com/apache/gravitino/pull/12006#discussion_r3666474466


##########
core/src/main/java/org/apache/gravitino/cache/CaffeineEntityCache.java:
##########
@@ -80,33 +85,34 @@ public class CaffeineEntityCache extends BaseEntityCache {
 
   private static final Logger LOG = 
LoggerFactory.getLogger(CaffeineEntityCache.class.getName());
 
+  /**
+   * Entity types that must not be cached by this implementation.
+   *
+   * <p>{@code USER}, {@code GROUP} and {@code ROLE} are materialized with 
relation-derived data
+   * joined in at load time: a role carries its securable objects, and a 
user/group carries its role
+   * names. A mutation on the entity itself invalidates its own key through 
the write path, but this
+   * embedded data also goes stale through a mutation on a different entity. 
For example, deleting
+   * or renaming a securable object changes a role's materialized form, and 
deleting or renaming a
+   * role changes a user's/group's role names. Such a mutation touches neither 
this entity's own key
+   * nor any hierarchy ancestor of it, so neither the write-path invalidation 
nor the prefix cascade
+   * in {@link #invalidateHierarchy} would evict it; only the (now removed) 
reverse index could.
+   * Caching them would therefore serve stale authorization data.
+   */
+  private static final Set<Entity.EntityType> NON_CACHEABLE_TYPES =
+      Sets.immutableEnumSet(
+          Entity.EntityType.USER, Entity.EntityType.GROUP, 
Entity.EntityType.ROLE);
+
   /** Segmented locking for better concurrency */
   private final SegmentedLock segmentedLock;
 
   /** Cache data structure. */
-  private final Cache<EntityCacheRelationKey, List<Entity>> cacheData;
-
-  /** Cache reverse index structure. */
-  private ReverseIndexCache reverseIndex;
+  private final Cache<EntityCacheKey, Entity> cacheData;
 
-  /** Cache Index structure. */
-  private RadixTree<EntityCacheRelationKey> cacheIndex;
+  /** Prefix index over cache keys, used for cascading removal of descendant 
entries. */
+  private RadixTree<EntityCacheKey> cacheIndex;

Review Comment:
   cacheIndex is handled outside the lock in size().



##########
core/src/main/java/org/apache/gravitino/cache/CaffeineEntityCache.java:
##########
@@ -30,34 +30,39 @@
 import com.googlecode.concurrenttrees.radix.ConcurrentRadixTree;
 import com.googlecode.concurrenttrees.radix.RadixTree;
 import 
com.googlecode.concurrenttrees.radix.node.concrete.DefaultCharArrayNodeFactory;
-import java.util.ArrayDeque;
-import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
-import java.util.Queue;
 import java.util.Set;
 import java.util.concurrent.ArrayBlockingQueue;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.ThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
-import java.util.stream.Collectors;
-import org.apache.commons.lang3.ArrayUtils;
-import org.apache.commons.lang3.StringUtils;
 import org.apache.gravitino.Config;
 import org.apache.gravitino.Configs;
 import org.apache.gravitino.Entity;
 import org.apache.gravitino.HasIdentifier;
 import org.apache.gravitino.NameIdentifier;
-import org.apache.gravitino.SupportsRelationOperations;
-import org.apache.gravitino.meta.GenericEntity;
 import org.apache.gravitino.meta.ModelVersionEntity;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-/** This class implements the {@link org.apache.gravitino.cache.EntityCache} 
using Caffeine */
+/**
+ * This class implements the {@link org.apache.gravitino.cache.EntityCache} 
using Caffeine.
+ *
+ * <p>The cache stores one entry per entity, keyed by the entity's {@code 
NameIdentifier} and type.
+ * A radix-tree prefix index over the cache keys implements cascading removal: 
invalidating an
+ * entity also drops every cached descendant entry (e.g. invalidating a 
catalog drops the cached
+ * schemas and tables under it).
+ *
+ * <p>Relation query results are NOT cached by this implementation; relation 
and list operations
+ * always fall back to the {@code EntityStore}. Entity types whose 
materialized form embeds
+ * relation-derived data ({@code USER}, {@code GROUP}, {@code ROLE}) are 
excluded from caching
+ * entirely, because without relation tracking their entries could not be 
invalidated when the
+ * referenced entities change.
+ */
 public class CaffeineEntityCache extends BaseEntityCache {

Review Comment:
   The function of withMultipleKeyCacheLock  is unused



##########
core/src/main/java/org/apache/gravitino/storage/relational/RelationalEntityStore.java:
##########
@@ -400,8 +318,13 @@ public void insertRelation(
       boolean override)
       throws IOException {
     backend.insertRelation(relType, srcIdentifier, srcType, dstIdentifier, 
dstType, override);
-    cache.invalidate(srcIdentifier, srcType, relType);
-    cache.invalidate(dstIdentifier, dstType, relType);
+    // Defensive: relation results are not cached, and no currently cached 
entity type embeds
+    // relation-derived data (the only types that do — USER/GROUP/ROLE — are 
excluded from the
+    // cache), so these invalidations are a no-op today. They are kept so that 
if a cached type ever
+    // starts materializing this relation, its stale single-entity entry is 
dropped on a relation
+    // write.

Review Comment:
   I don't think this comment is accurate anymore. Specifically, *"so these 
invalidations are a no-op today"* doesn't appear to be correct.



##########
core/src/main/java/org/apache/gravitino/cache/CaffeineEntityCache.java:
##########
@@ -80,33 +85,34 @@ public class CaffeineEntityCache extends BaseEntityCache {
 
   private static final Logger LOG = 
LoggerFactory.getLogger(CaffeineEntityCache.class.getName());
 
+  /**
+   * Entity types that must not be cached by this implementation.
+   *
+   * <p>{@code USER}, {@code GROUP} and {@code ROLE} are materialized with 
relation-derived data
+   * joined in at load time: a role carries its securable objects, and a 
user/group carries its role
+   * names. A mutation on the entity itself invalidates its own key through 
the write path, but this
+   * embedded data also goes stale through a mutation on a different entity. 
For example, deleting
+   * or renaming a securable object changes a role's materialized form, and 
deleting or renaming a
+   * role changes a user's/group's role names. Such a mutation touches neither 
this entity's own key
+   * nor any hierarchy ancestor of it, so neither the write-path invalidation 
nor the prefix cascade
+   * in {@link #invalidateHierarchy} would evict it; only the (now removed) 
reverse index could.
+   * Caching them would therefore serve stale authorization data.
+   */
+  private static final Set<Entity.EntityType> NON_CACHEABLE_TYPES =
+      Sets.immutableEnumSet(
+          Entity.EntityType.USER, Entity.EntityType.GROUP, 
Entity.EntityType.ROLE);

Review Comment:
   NON_CACHEABLE_TYPES (USER/GROUP/ROLE) is a correctness constraint — caching 
them serves stale authorization data. But it's a private field in 
CaffeineEntityCache, while SupportsEntityStoreCache#put documents nothing about 
it. Since this PR aims to make the SPI re-implementable for #11737, suggest 
moving the check to BaseEntityCache.put and documenting it on the interface.



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