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 4ea6d94ca7 [#7177] feat(core): Add a Caffeine-based implementation for 
EntityCache (#7330)
4ea6d94ca7 is described below

commit 4ea6d94ca7f317990307f4bca680681366b3ab11
Author: Lord of Abyss <[email protected]>
AuthorDate: Wed Jun 18 00:09:59 2025 +0800

    [#7177] feat(core): Add a Caffeine-based implementation for EntityCache 
(#7330)
    
    ### What changes were proposed in this pull request?
    
    Fix some bugs in EntityCache and add test cases.
    
    1. Move the database access logic to `BaseEntityCache` for better reuse
    and abstraction.
    2. Add an `allFields` parameter to `loadEntitiesByRelation` to align
    with `SupportsRelationOperations#listEntitiesByRelation`.
    3. Add an entityCache field to `GravitinoEnv` for direct access to the
    cache from components.
    
    - [X] Implement the methods related to the `SupportsEntityStoreCache`
    and `SupportsRelationEntityCache` interface in `CaffeineEntityCache`.
    - [X] Add unit tests for both index and cache.
    - [ ] Use JCStress to perform multi-threaded testing on
    `CaffeineEntityCache` and the `CacheIndex`.
    
    | **Category** | **Test Case Description** | **Method(s)** |
    | ----------------------- |
    ------------------------------------------------- |
    ------------------------------------------------------------ |
    | **Basic Functionality** | Basic put/get/getIfPresent operations |
    `testPutAndGet`, `testGetIfPresent` |
    | | Same identifier with different entity types |
    `testPutSameIdentifierEntities` |
    | | Size counting | `testSize` |
    | | Clear the cache | `testClear` |
    | **Invalidation** | Invalidate by METALAKE/CATALOG/SCHEMA/TABLE level |
    `testInvalidateMetalake`, `testInvalidateCatalog`,
    `testInvalidateSchema`, `testInvalidateTable` |
    | | Invalidate non-existent entity | `testRemoveNonExistentEntity` |
    | **Relation Handling** | Put/Get/Invalidate entity relations | Covered
    in multiple tests (e.g., `testPutAndGet`, `testGetIfPresent`) |
    | **Eviction Policies** | Expire by time | `testExpireByTime` |
    | | Expire by weight | `testExpireByWeight` |
    | | Exceed max weight immediately | `testExpireByWeightExceedMaxWeight`
    |
    | | Expire by size | `testExpireBySize` |
    | **Weight Logic** | Correctness of entity weight calculation |
    `testWeightCalculation` |
    | **Error & Boundary** | Null argument checks |
    `testGetIfPresentWithNull`, `testContainsWithNull`,
    `testInvalidateWithNull`, `testPutWithNull` |
    
    ### Why are the changes needed?
    
    Fix: #7177
    
    ### Does this PR introduce _any_ user-facing change?
    
    no
    
    ### How was this patch tested?
    
    local test.
---
 .../hadoop/TestHadoopCatalogOperations.java        |   1 +
 .../catalog/kafka/TestKafkaCatalogOperations.java  |   1 +
 .../catalog/model/TestModelCatalogOperations.java  |   1 +
 .../main/java/org/apache/gravitino/Configs.java    |   9 +
 .../org/apache/gravitino/cache/CacheFactory.java   |  55 +++
 .../storage/relational/RelationalEntityStore.java  |   5 +-
 .../authorization/TestAccessControlManager.java    |   1 +
 .../gravitino/authorization/TestOwnerManager.java  |   1 +
 .../apache/gravitino/cache/TestCacheConfig.java    |   1 +
 ...{TestCacheConfig.java => TestCacheFactory.java} |  32 +-
 .../org/apache/gravitino/cache/TestCacheIndex.java | 226 ++++++++++
 .../gravitino/cache/TestCaffeineEntityCache.java   | 454 +++++++++++++++++++--
 .../gravitino/storage/TestEntityStorage.java       |   1 +
 .../org/apache/gravitino/tag/TestTagManager.java   |   1 +
 14 files changed, 743 insertions(+), 46 deletions(-)

diff --git 
a/catalogs/catalog-hadoop/src/test/java/org/apache/gravitino/catalog/hadoop/TestHadoopCatalogOperations.java
 
b/catalogs/catalog-hadoop/src/test/java/org/apache/gravitino/catalog/hadoop/TestHadoopCatalogOperations.java
index 4b1b936632..35c34a07db 100644
--- 
a/catalogs/catalog-hadoop/src/test/java/org/apache/gravitino/catalog/hadoop/TestHadoopCatalogOperations.java
+++ 
b/catalogs/catalog-hadoop/src/test/java/org/apache/gravitino/catalog/hadoop/TestHadoopCatalogOperations.java
@@ -226,6 +226,7 @@ public class TestHadoopCatalogOperations {
     
Mockito.when(config.get(Configs.CACHE_EXPIRATION_TIME)).thenReturn(3_600_000L);
     Mockito.when(config.get(Configs.CACHE_WEIGHER_ENABLED)).thenReturn(true);
     Mockito.when(config.get(Configs.CACHE_STATS_ENABLED)).thenReturn(false);
+    
Mockito.when(config.get(Configs.CACHE_IMPLEMENTATION)).thenReturn("caffeine");
 
     store = EntityStoreFactory.createEntityStore(config);
     store.initialize(config);
diff --git 
a/catalogs/catalog-kafka/src/test/java/org/apache/gravitino/catalog/kafka/TestKafkaCatalogOperations.java
 
b/catalogs/catalog-kafka/src/test/java/org/apache/gravitino/catalog/kafka/TestKafkaCatalogOperations.java
index e820b8dd4d..007477271d 100644
--- 
a/catalogs/catalog-kafka/src/test/java/org/apache/gravitino/catalog/kafka/TestKafkaCatalogOperations.java
+++ 
b/catalogs/catalog-kafka/src/test/java/org/apache/gravitino/catalog/kafka/TestKafkaCatalogOperations.java
@@ -157,6 +157,7 @@ public class TestKafkaCatalogOperations extends 
KafkaClusterEmbedded {
     
Mockito.when(config.get(Configs.CACHE_EXPIRATION_TIME)).thenReturn(3_600_000L);
     Mockito.when(config.get(Configs.CACHE_WEIGHER_ENABLED)).thenReturn(true);
     Mockito.when(config.get(Configs.CACHE_STATS_ENABLED)).thenReturn(false);
+    
Mockito.when(config.get(Configs.CACHE_IMPLEMENTATION)).thenReturn("caffeine");
 
     // Mock
     MetalakeMetaService metalakeMetaService = 
MetalakeMetaService.getInstance();
diff --git 
a/catalogs/catalog-model/src/test/java/org/apache/gravtitino/catalog/model/TestModelCatalogOperations.java
 
b/catalogs/catalog-model/src/test/java/org/apache/gravtitino/catalog/model/TestModelCatalogOperations.java
index 709999fd81..930e29f8ad 100644
--- 
a/catalogs/catalog-model/src/test/java/org/apache/gravtitino/catalog/model/TestModelCatalogOperations.java
+++ 
b/catalogs/catalog-model/src/test/java/org/apache/gravtitino/catalog/model/TestModelCatalogOperations.java
@@ -126,6 +126,7 @@ public class TestModelCatalogOperations {
     
Mockito.when(config.get(Configs.CACHE_EXPIRATION_TIME)).thenReturn(3_600_000L);
     Mockito.when(config.get(Configs.CACHE_WEIGHER_ENABLED)).thenReturn(true);
     Mockito.when(config.get(Configs.CACHE_STATS_ENABLED)).thenReturn(false);
+    
Mockito.when(config.get(Configs.CACHE_IMPLEMENTATION)).thenReturn("caffeine");
 
     store = EntityStoreFactory.createEntityStore(config);
     store.initialize(config);
diff --git a/core/src/main/java/org/apache/gravitino/Configs.java 
b/core/src/main/java/org/apache/gravitino/Configs.java
index 95af2e0f0f..76f5235f6f 100644
--- a/core/src/main/java/org/apache/gravitino/Configs.java
+++ b/core/src/main/java/org/apache/gravitino/Configs.java
@@ -389,4 +389,13 @@ public class Configs {
           .version(ConfigConstants.VERSION_1_0_0)
           .booleanConf()
           .createWithDefault(true);
+
+  // Provider name for cache
+  public static final ConfigEntry<String> CACHE_IMPLEMENTATION =
+      new ConfigBuilder("gravitino.cache.implementation")
+          .doc("Which cache implementation to use")
+          .version(ConfigConstants.VERSION_1_0_0)
+          .stringConf()
+          .checkValue(StringUtils::isNotBlank, 
ConfigConstants.NOT_BLANK_ERROR_MSG)
+          .createWithDefault("caffeine");
 }
diff --git a/core/src/main/java/org/apache/gravitino/cache/CacheFactory.java 
b/core/src/main/java/org/apache/gravitino/cache/CacheFactory.java
new file mode 100644
index 0000000000..db4ff36374
--- /dev/null
+++ b/core/src/main/java/org/apache/gravitino/cache/CacheFactory.java
@@ -0,0 +1,55 @@
+/*
+ * 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.cache;
+
+import com.google.common.collect.ImmutableMap;
+import org.apache.gravitino.Config;
+import org.apache.gravitino.Configs;
+
+/** Factory class for creating {@link org.apache.gravitino.cache.EntityCache} 
instances. */
+public final class CacheFactory {
+
+  // Register EntityCache's short name to its full qualified class name in the 
map. So that user
+  // doesn't need to specify the full qualified class name when creating an 
EntityCache instance.
+  public static final ImmutableMap<String, String> ENTITY_CACHES =
+      ImmutableMap.of("caffeine", 
CaffeineEntityCache.class.getCanonicalName());
+
+  // Private constructor to prevent instantiation of this factory class.
+  private CacheFactory() {}
+
+  /**
+   * Creates a new {@link org.apache.gravitino.cache.EntityCache} using the 
cache type specified in
+   * the configuration.
+   *
+   * @param config The configuration.
+   * @return A cache instance
+   */
+  public static EntityCache getEntityCache(Config config) {
+    String name = config.get(Configs.CACHE_IMPLEMENTATION);
+    String className = ENTITY_CACHES.getOrDefault(name, name);
+
+    try {
+      return (EntityCache)
+          
Class.forName(className).getDeclaredConstructor(Config.class).newInstance(config);
+    } catch (Exception e) {
+      throw new RuntimeException("Failed to create and initialize EntityCache: 
" + name, e);
+    }
+  }
+}
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 2bc2075f70..296629e518 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
@@ -35,7 +35,7 @@ import org.apache.gravitino.MetadataObject;
 import org.apache.gravitino.NameIdentifier;
 import org.apache.gravitino.Namespace;
 import org.apache.gravitino.SupportsRelationOperations;
-import org.apache.gravitino.cache.CaffeineEntityCache;
+import org.apache.gravitino.cache.CacheFactory;
 import org.apache.gravitino.cache.EntityCache;
 import org.apache.gravitino.cache.NoOpsCache;
 import org.apache.gravitino.exceptions.NoSuchEntityException;
@@ -65,10 +65,9 @@ public class RelationalEntityStore
     this.backend = createRelationalEntityBackend(config);
     this.garbageCollector = new RelationalGarbageCollector(backend, config);
     this.garbageCollector.start();
-    // TODO USE SPI to load the cache
     this.cache =
         config.get(Configs.CACHE_ENABLED)
-            ? new CaffeineEntityCache(config)
+            ? CacheFactory.getEntityCache(config)
             : new NoOpsCache(config);
   }
 
diff --git 
a/core/src/test/java/org/apache/gravitino/authorization/TestAccessControlManager.java
 
b/core/src/test/java/org/apache/gravitino/authorization/TestAccessControlManager.java
index e93deab223..21d4dcb477 100644
--- 
a/core/src/test/java/org/apache/gravitino/authorization/TestAccessControlManager.java
+++ 
b/core/src/test/java/org/apache/gravitino/authorization/TestAccessControlManager.java
@@ -137,6 +137,7 @@ public class TestAccessControlManager {
     
Mockito.when(config.get(Configs.CACHE_EXPIRATION_TIME)).thenReturn(3_600_000L);
     Mockito.when(config.get(Configs.CACHE_WEIGHER_ENABLED)).thenReturn(true);
     Mockito.when(config.get(Configs.CACHE_STATS_ENABLED)).thenReturn(false);
+    
Mockito.when(config.get(Configs.CACHE_IMPLEMENTATION)).thenReturn("caffeine");
 
     Mockito.doReturn(100000L).when(config).get(TREE_LOCK_MAX_NODE_IN_MEMORY);
     Mockito.doReturn(1000L).when(config).get(TREE_LOCK_MIN_NODE_IN_MEMORY);
diff --git 
a/core/src/test/java/org/apache/gravitino/authorization/TestOwnerManager.java 
b/core/src/test/java/org/apache/gravitino/authorization/TestOwnerManager.java
index 95dd6fcd21..889e4a120f 100644
--- 
a/core/src/test/java/org/apache/gravitino/authorization/TestOwnerManager.java
+++ 
b/core/src/test/java/org/apache/gravitino/authorization/TestOwnerManager.java
@@ -110,6 +110,7 @@ public class TestOwnerManager {
     
Mockito.when(config.get(Configs.CACHE_EXPIRATION_TIME)).thenReturn(3_600_000L);
     Mockito.when(config.get(Configs.CACHE_WEIGHER_ENABLED)).thenReturn(true);
     Mockito.when(config.get(Configs.CACHE_STATS_ENABLED)).thenReturn(false);
+    
Mockito.when(config.get(Configs.CACHE_IMPLEMENTATION)).thenReturn("caffeine");
 
     Mockito.doReturn(100000L).when(config).get(TREE_LOCK_MAX_NODE_IN_MEMORY);
     Mockito.doReturn(1000L).when(config).get(TREE_LOCK_MIN_NODE_IN_MEMORY);
diff --git a/core/src/test/java/org/apache/gravitino/cache/TestCacheConfig.java 
b/core/src/test/java/org/apache/gravitino/cache/TestCacheConfig.java
index 4316b777f5..50d714d1fc 100644
--- a/core/src/test/java/org/apache/gravitino/cache/TestCacheConfig.java
+++ b/core/src/test/java/org/apache/gravitino/cache/TestCacheConfig.java
@@ -37,6 +37,7 @@ public class TestCacheConfig {
     Assertions.assertEquals(10_000, config.get(Configs.CACHE_MAX_ENTRIES));
     Assertions.assertEquals(3_600_000L, 
config.get(Configs.CACHE_EXPIRATION_TIME));
     Assertions.assertEquals(200_302_000L, EntityCacheWeigher.getMaxWeight());
+    Assertions.assertEquals("caffeine", 
config.get(Configs.CACHE_IMPLEMENTATION));
   }
 
   @Test
diff --git a/core/src/test/java/org/apache/gravitino/cache/TestCacheConfig.java 
b/core/src/test/java/org/apache/gravitino/cache/TestCacheFactory.java
similarity index 53%
copy from core/src/test/java/org/apache/gravitino/cache/TestCacheConfig.java
copy to core/src/test/java/org/apache/gravitino/cache/TestCacheFactory.java
index 4316b777f5..562c2d9f13 100644
--- a/core/src/test/java/org/apache/gravitino/cache/TestCacheConfig.java
+++ b/core/src/test/java/org/apache/gravitino/cache/TestCacheFactory.java
@@ -19,31 +19,29 @@
 
 package org.apache.gravitino.cache;
 
-import com.google.common.collect.Lists;
-import java.util.ArrayList;
-import java.util.Optional;
 import org.apache.gravitino.Config;
 import org.apache.gravitino.Configs;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
+
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+public class TestCacheFactory {
 
-public class TestCacheConfig {
   @Test
-  void testDefaultCacheConfig() {
-    Config config = new Config(false) {};
-    Assertions.assertFalse(config.get(Configs.CACHE_STATS_ENABLED));
-    Assertions.assertTrue(config.get(Configs.CACHE_ENABLED));
-    Assertions.assertTrue(config.get(Configs.CACHE_WEIGHER_ENABLED));
-    Assertions.assertEquals(10_000, config.get(Configs.CACHE_MAX_ENTRIES));
-    Assertions.assertEquals(3_600_000L, 
config.get(Configs.CACHE_EXPIRATION_TIME));
-    Assertions.assertEquals(200_302_000L, EntityCacheWeigher.getMaxWeight());
+  void testGetCache() {
+    Config config = new Config() {};
+    EntityCache entityCache = CacheFactory.getEntityCache(config);
+    Assertions.assertInstanceOf(CaffeineEntityCache.class, entityCache);
+
+    entityCache = CacheFactory.getEntityCache(config);
+    Assertions.assertInstanceOf(CaffeineEntityCache.class, entityCache);
   }
 
   @Test
-  void test() {
-    ArrayList<Integer> list = Lists.newArrayList();
-    Optional<Integer> i =
-        Optional.ofNullable(list).filter(entities -> 
!entities.isEmpty()).map(l -> l.get(0));
-    Assertions.assertFalse(i.isPresent());
+  void testCreateCacheWithInvalidName() {
+    Config config = new Config() {};
+    config.set(Configs.CACHE_IMPLEMENTATION, "InvalidCacheName");
+    Assertions.assertThrows(RuntimeException.class, () -> 
CacheFactory.getEntityCache(config));
   }
 }
diff --git a/core/src/test/java/org/apache/gravitino/cache/TestCacheIndex.java 
b/core/src/test/java/org/apache/gravitino/cache/TestCacheIndex.java
new file mode 100644
index 0000000000..d87995fdea
--- /dev/null
+++ b/core/src/test/java/org/apache/gravitino/cache/TestCacheIndex.java
@@ -0,0 +1,226 @@
+/*
+ * 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.cache;
+
+import com.google.common.collect.ImmutableList;
+import com.googlecode.concurrenttrees.radix.ConcurrentRadixTree;
+import com.googlecode.concurrenttrees.radix.RadixTree;
+import 
com.googlecode.concurrenttrees.radix.node.concrete.DefaultCharArrayNodeFactory;
+import java.util.List;
+import org.apache.gravitino.Entity;
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.SupportsRelationOperations;
+import org.apache.gravitino.utils.NameIdentifierUtil;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+public class TestCacheIndex {
+  private RadixTree<EntityCacheKey> indexTree;
+
+  private NameIdentifier ident1;
+  private NameIdentifier ident2;
+  private NameIdentifier ident3;
+  private NameIdentifier ident4;
+  private NameIdentifier ident5;
+  private NameIdentifier ident6;
+  private NameIdentifier ident7;
+  private NameIdentifier ident8;
+  private NameIdentifier ident9;
+  private NameIdentifier ident10;
+  private NameIdentifier ident11;
+  private NameIdentifier ident12;
+
+  private EntityCacheKey key1;
+  private EntityCacheKey key2;
+  private EntityCacheKey key3;
+  private EntityCacheKey key4;
+  private EntityCacheKey key5;
+  private EntityCacheKey key6;
+  private EntityCacheKey key7;
+  private EntityCacheKey key8;
+  private EntityCacheKey key9;
+  private EntityCacheKey key10;
+  private EntityCacheKey key11;
+  private EntityCacheKey key12;
+
+  @BeforeEach
+  void setUp() {
+    indexTree = new ConcurrentRadixTree<>(new DefaultCharArrayNodeFactory());
+    ident1 = NameIdentifier.of("metalake1", "catalog1", "schema1");
+    ident2 = NameIdentifier.of("metalake2", "catalog2", "schema2");
+    ident3 = NameIdentifier.of("metalake1", "catalog1", "schema1", "table");
+    ident4 = NameIdentifier.of("metalake1", "catalog1", "schema1", "topic");
+    ident5 = NameIdentifier.of("metalake1", "catalog2", "schema1", "table");
+    ident6 = NameIdentifier.of("metalake1", "catalog1", "schema2", "table");
+
+    ident7 = NameIdentifierUtil.ofRole("metalake1", "role1");
+    ident8 = NameIdentifierUtil.ofRole("metalake2", "role2");
+
+    ident9 = NameIdentifierUtil.ofGroup("metalake1", "group1");
+    ident10 = NameIdentifierUtil.ofGroup("metalake1", "group2");
+
+    ident11 = NameIdentifierUtil.ofUser("metalake2", "user1");
+    ident12 = NameIdentifierUtil.ofUser("metalake2", "user2");
+
+    key1 = EntityCacheKey.of(ident1, Entity.EntityType.SCHEMA);
+    key2 = EntityCacheKey.of(ident2, Entity.EntityType.SCHEMA);
+    key3 = EntityCacheKey.of(ident3, Entity.EntityType.TABLE);
+    key4 = EntityCacheKey.of(ident4, Entity.EntityType.TOPIC);
+    key5 = EntityCacheKey.of(ident5, Entity.EntityType.TABLE);
+    key6 = EntityCacheKey.of(ident6, Entity.EntityType.TABLE);
+
+    key7 =
+        EntityCacheKey.of(
+            ident7, Entity.EntityType.ROLE, 
SupportsRelationOperations.Type.ROLE_GROUP_REL);
+    key8 =
+        EntityCacheKey.of(
+            ident8, Entity.EntityType.ROLE, 
SupportsRelationOperations.Type.ROLE_USER_REL);
+
+    key9 = EntityCacheKey.of(ident9, Entity.EntityType.GROUP);
+    key10 = EntityCacheKey.of(ident10, Entity.EntityType.GROUP);
+    key11 = EntityCacheKey.of(ident11, Entity.EntityType.USER);
+    key12 = EntityCacheKey.of(ident12, Entity.EntityType.USER);
+
+    addIndex(indexTree, key12);
+    addIndex(indexTree, key11);
+    addIndex(indexTree, key10);
+    addIndex(indexTree, key9);
+    addIndex(indexTree, key8);
+    addIndex(indexTree, key7);
+    addIndex(indexTree, key6);
+    addIndex(indexTree, key5);
+    addIndex(indexTree, key4);
+    addIndex(indexTree, key3);
+    addIndex(indexTree, key2);
+    addIndex(indexTree, key1);
+  }
+
+  @Test
+  void testAddIndex() {
+    Assertions.assertEquals(12, indexTree.size());
+  }
+
+  @Test
+  void testGetFromByMetalakePrefix() {
+    List<EntityCacheKey> storeEntityCacheKeys =
+        
ImmutableList.copyOf(indexTree.getValuesForKeysStartingWith("metalake1"));
+
+    Assertions.assertEquals(8, storeEntityCacheKeys.size());
+    Assertions.assertTrue(storeEntityCacheKeys.contains(key1));
+    Assertions.assertTrue(storeEntityCacheKeys.contains(key3));
+    Assertions.assertTrue(storeEntityCacheKeys.contains(key4));
+    Assertions.assertTrue(storeEntityCacheKeys.contains(key5));
+    Assertions.assertTrue(storeEntityCacheKeys.contains(key6));
+    Assertions.assertTrue(storeEntityCacheKeys.contains(key7));
+    Assertions.assertTrue(storeEntityCacheKeys.contains(key9));
+    Assertions.assertTrue(storeEntityCacheKeys.contains(key10));
+
+    List<EntityCacheKey> storeEntityCacheKeys2 =
+        
ImmutableList.copyOf(indexTree.getValuesForKeysStartingWith("metalake2"));
+
+    Assertions.assertEquals(4, storeEntityCacheKeys2.size());
+    Assertions.assertTrue(storeEntityCacheKeys2.contains(key2));
+    Assertions.assertTrue(storeEntityCacheKeys2.contains(key8));
+    Assertions.assertTrue(storeEntityCacheKeys2.contains(key11));
+    Assertions.assertTrue(storeEntityCacheKeys2.contains(key12));
+  }
+
+  @Test
+  void testGetByCatalogPrefix() {
+    List<EntityCacheKey> storeEntityCacheKeys =
+        
ImmutableList.copyOf(indexTree.getValuesForKeysStartingWith("metalake1.catalog1"));
+
+    Assertions.assertEquals(4, storeEntityCacheKeys.size());
+    Assertions.assertTrue(storeEntityCacheKeys.contains(key1));
+    Assertions.assertTrue(storeEntityCacheKeys.contains(key3));
+    Assertions.assertTrue(storeEntityCacheKeys.contains(key4));
+    Assertions.assertTrue(storeEntityCacheKeys.contains(key6));
+
+    storeEntityCacheKeys =
+        
ImmutableList.copyOf(indexTree.getValuesForKeysStartingWith("metalake1.catalog2"));
+    Assertions.assertEquals(1, storeEntityCacheKeys.size());
+    Assertions.assertTrue(storeEntityCacheKeys.contains(key5));
+  }
+
+  @Test
+  void testGetBySchemaPrefix() {
+    List<EntityCacheKey> storeEntityCacheKeys =
+        
ImmutableList.copyOf(indexTree.getValuesForKeysStartingWith("metalake1.catalog1.schema1"));
+
+    Assertions.assertEquals(3, storeEntityCacheKeys.size());
+    Assertions.assertTrue(storeEntityCacheKeys.contains(key1));
+    Assertions.assertTrue(storeEntityCacheKeys.contains(key3));
+    Assertions.assertTrue(storeEntityCacheKeys.contains(key4));
+
+    storeEntityCacheKeys =
+        
ImmutableList.copyOf(indexTree.getValuesForKeysStartingWith("metalake1.catalog1.schema2"));
+    Assertions.assertEquals(1, storeEntityCacheKeys.size());
+    Assertions.assertTrue(storeEntityCacheKeys.contains(key6));
+
+    storeEntityCacheKeys =
+        
ImmutableList.copyOf(indexTree.getValuesForKeysStartingWith("metalake1.catalog2.schema1"));
+    Assertions.assertEquals(1, storeEntityCacheKeys.size());
+    Assertions.assertTrue(storeEntityCacheKeys.contains(key5));
+  }
+
+  @Test
+  void testGetByExactKey() {
+    EntityCacheKey storeEntityCacheKey = 
indexTree.getValueForExactKey(key1.toString());
+    Assertions.assertEquals(key1, storeEntityCacheKey);
+
+    storeEntityCacheKey = indexTree.getValueForExactKey(key2.toString());
+    Assertions.assertEquals(key2, storeEntityCacheKey);
+
+    storeEntityCacheKey = indexTree.getValueForExactKey(key3.toString());
+    Assertions.assertEquals(key3, storeEntityCacheKey);
+
+    storeEntityCacheKey = indexTree.getValueForExactKey(key4.toString());
+    Assertions.assertEquals(key4, storeEntityCacheKey);
+
+    storeEntityCacheKey = indexTree.getValueForExactKey(key5.toString());
+    Assertions.assertEquals(key5, storeEntityCacheKey);
+
+    storeEntityCacheKey = indexTree.getValueForExactKey(key6.toString());
+    Assertions.assertEquals(key6, storeEntityCacheKey);
+
+    storeEntityCacheKey = indexTree.getValueForExactKey(key7.toString());
+    Assertions.assertEquals(key7, storeEntityCacheKey);
+
+    storeEntityCacheKey = indexTree.getValueForExactKey(key8.toString());
+    Assertions.assertEquals(key8, storeEntityCacheKey);
+
+    storeEntityCacheKey = indexTree.getValueForExactKey(key9.toString());
+    Assertions.assertEquals(key9, storeEntityCacheKey);
+
+    storeEntityCacheKey = indexTree.getValueForExactKey(key10.toString());
+    Assertions.assertEquals(key10, storeEntityCacheKey);
+
+    storeEntityCacheKey = indexTree.getValueForExactKey(key11.toString());
+    Assertions.assertEquals(key11, storeEntityCacheKey);
+
+    storeEntityCacheKey = indexTree.getValueForExactKey(key12.toString());
+    Assertions.assertEquals(key12, storeEntityCacheKey);
+  }
+
+  private void addIndex(RadixTree<EntityCacheKey> indexTree, EntityCacheKey 
storeEntityCacheKey) {
+    indexTree.put(storeEntityCacheKey.toString(), storeEntityCacheKey);
+  }
+}
diff --git 
a/core/src/test/java/org/apache/gravitino/cache/TestCaffeineEntityCache.java 
b/core/src/test/java/org/apache/gravitino/cache/TestCaffeineEntityCache.java
index 57a60f1f4b..68e414bf7c 100644
--- a/core/src/test/java/org/apache/gravitino/cache/TestCaffeineEntityCache.java
+++ b/core/src/test/java/org/apache/gravitino/cache/TestCaffeineEntityCache.java
@@ -19,10 +19,12 @@
 
 package org.apache.gravitino.cache;
 
-import static org.mockito.Mockito.spy;
-
+import com.github.benmanes.caffeine.cache.Cache;
 import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.apache.commons.lang3.reflect.FieldUtils;
 import org.apache.gravitino.Config;
+import org.apache.gravitino.Configs;
 import org.apache.gravitino.Entity;
 import org.apache.gravitino.NameIdentifier;
 import org.apache.gravitino.Namespace;
@@ -48,9 +50,6 @@ import org.junit.jupiter.api.TestInstance;
 
 @TestInstance(TestInstance.Lifecycle.PER_CLASS)
 public class TestCaffeineEntityCache {
-  private CaffeineEntityCache real;
-  private CaffeineEntityCache cache;
-
   private NameIdentifier ident1;
   private NameIdentifier ident2;
   private NameIdentifier ident3;
@@ -80,6 +79,19 @@ public class TestCaffeineEntityCache {
   private RoleEntity entity12;
   private RoleEntity entity13;
 
+  private static Object getCacheDataFrom(EntityCache cache) {
+    try {
+      Object object = FieldUtils.readDeclaredField(cache, "cacheData", true);
+      if (object instanceof Cache) {
+        return object;
+      } else {
+        throw new RuntimeException("Unexpected cache data type: " + 
object.getClass());
+      }
+    } catch (IllegalAccessException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
   @BeforeAll
   void init() {
     initTestNameIdentifier();
@@ -88,8 +100,7 @@ public class TestCaffeineEntityCache {
 
   @Test
   void testPutAllTypeInCache() {
-
-    initCache();
+    EntityCache cache = new CaffeineEntityCache(new Config() {});
 
     BaseMetalake testMetalake = TestUtil.getTestMetalake();
     CatalogEntity testCatalogEntity = TestUtil.getTestCatalogEntity();
@@ -179,9 +190,74 @@ public class TestCaffeineEntityCache {
   }
 
   @Test
-  void testGetIfPresent() {
+  void testPutSameIdentifierEntities() {
+    EntityCache cache = new CaffeineEntityCache(new Config() {});
+
+    UserEntity testUserEntity = TestUtil.getTestUserEntity();
+    TableEntity testTableEntity =
+        TestUtil.getTestTableEntity(
+            12L, "test_user", Namespace.of("test_metalake", "system", "user"));
+
+    cache.put(testUserEntity);
+    cache.put(testTableEntity);
+
+    Assertions.assertEquals(2, cache.size());
+    Assertions.assertTrue(
+        cache.contains(testTableEntity.nameIdentifier(), 
Entity.EntityType.TABLE));
+    Assertions.assertTrue(cache.contains(testUserEntity.nameIdentifier(), 
Entity.EntityType.USER));
+  }
+
+  @Test
+  void testPutAndGet() {
+    EntityCache cache = new CaffeineEntityCache(new Config() {});
+
+    cache.put(entity1);
+    cache.put(entity2);
+    cache.put(entity3);
+    cache.put(entity4);
+    cache.put(entity5);
+    cache.put(entity6);
+    cache.put(entity7);
+    cache.put(entity8);
+    cache.put(entity9);
+    cache.put(entity10);
+    cache.put(entity11);
+
+    cache.put(
+        entity12.nameIdentifier(),
+        Entity.EntityType.ROLE,
+        SupportsRelationOperations.Type.ROLE_USER_REL,
+        ImmutableList.of(entity8, entity9));
+    cache.put(
+        entity13.nameIdentifier(),
+        Entity.EntityType.ROLE,
+        SupportsRelationOperations.Type.ROLE_GROUP_REL,
+        ImmutableList.of(entity10, entity11));
+
+    Assertions.assertTrue(cache.contains(ident1, Entity.EntityType.SCHEMA));
+    Assertions.assertTrue(cache.contains(ident2, Entity.EntityType.SCHEMA));
+    Assertions.assertTrue(cache.contains(ident3, Entity.EntityType.TABLE));
+    Assertions.assertTrue(cache.contains(ident4, Entity.EntityType.TABLE));
+    Assertions.assertTrue(cache.contains(ident5, Entity.EntityType.TABLE));
+    Assertions.assertTrue(cache.contains(ident6, Entity.EntityType.CATALOG));
+    Assertions.assertTrue(cache.contains(ident7, Entity.EntityType.METALAKE));
+
+    Assertions.assertTrue(cache.contains(ident8, Entity.EntityType.USER));
+    Assertions.assertTrue(cache.contains(ident9, Entity.EntityType.USER));
+    Assertions.assertTrue(cache.contains(ident10, Entity.EntityType.GROUP));
+    Assertions.assertTrue(cache.contains(ident11, Entity.EntityType.GROUP));
+
+    Assertions.assertTrue(
+        cache.contains(
+            ident12, Entity.EntityType.ROLE, 
SupportsRelationOperations.Type.ROLE_USER_REL));
+    Assertions.assertTrue(
+        cache.contains(
+            ident13, Entity.EntityType.ROLE, 
SupportsRelationOperations.Type.ROLE_GROUP_REL));
+  }
 
-    initCache();
+  @Test
+  void testGetIfPresent() {
+    EntityCache cache = new CaffeineEntityCache(new Config() {});
 
     cache.put(entity1);
     cache.put(entity2);
@@ -210,8 +286,7 @@ public class TestCaffeineEntityCache {
 
   @Test
   void testContains() {
-
-    initCache();
+    EntityCache cache = new CaffeineEntityCache(new Config() {});
 
     cache.put(entity1);
     cache.put(entity2);
@@ -228,8 +303,7 @@ public class TestCaffeineEntityCache {
 
   @Test
   void testSize() {
-
-    initCache();
+    EntityCache cache = new CaffeineEntityCache(new Config() {});
 
     cache.put(entity1);
     cache.put(entity2);
@@ -240,8 +314,7 @@ public class TestCaffeineEntityCache {
 
   @Test
   void testClear() {
-
-    initCache();
+    EntityCache cache = new CaffeineEntityCache(new Config() {});
 
     cache.put(entity1);
     cache.put(entity2);
@@ -287,8 +360,7 @@ public class TestCaffeineEntityCache {
 
   @Test
   void testInvalidateMetalake() {
-
-    initCache();
+    EntityCache cache = new CaffeineEntityCache(new Config() {});
 
     cache.put(entity1);
     cache.put(entity2);
@@ -333,15 +405,348 @@ public class TestCaffeineEntityCache {
     Assertions.assertFalse(cache.contains(ident7, Entity.EntityType.TABLE));
   }
 
-  private void initCache() {
-    initCache(new Config() {});
+  @Test
+  void testInvalidateCatalog() {
+    EntityCache cache = new CaffeineEntityCache(new Config() {});
+
+    cache.put(entity1);
+    cache.put(entity2);
+    cache.put(entity3);
+    cache.put(entity4);
+    cache.put(entity5);
+    cache.put(entity6);
+    cache.put(entity7);
+
+    Assertions.assertEquals(7, cache.size());
+    Assertions.assertTrue(cache.contains(ident1, Entity.EntityType.SCHEMA));
+    Assertions.assertTrue(cache.contains(ident2, Entity.EntityType.SCHEMA));
+    Assertions.assertTrue(cache.contains(ident3, Entity.EntityType.TABLE));
+    Assertions.assertTrue(cache.contains(ident4, Entity.EntityType.TABLE));
+    Assertions.assertTrue(cache.contains(ident5, Entity.EntityType.TABLE));
+    Assertions.assertTrue(cache.contains(ident6, Entity.EntityType.CATALOG));
+    Assertions.assertTrue(cache.contains(ident7, Entity.EntityType.METALAKE));
+
+    cache.invalidate(ident6, Entity.EntityType.CATALOG);
+    Assertions.assertEquals(3, cache.size());
+
+    Assertions.assertTrue(cache.getIfPresent(ident7, 
Entity.EntityType.METALAKE).isPresent());
+    Assertions.assertTrue(cache.getIfPresent(ident4, 
Entity.EntityType.TABLE).isPresent());
+    Assertions.assertTrue(cache.getIfPresent(ident2, 
Entity.EntityType.SCHEMA).isPresent());
+
+    Assertions.assertFalse(cache.getIfPresent(ident1, 
Entity.EntityType.SCHEMA).isPresent());
+    Assertions.assertFalse(cache.getIfPresent(ident3, 
Entity.EntityType.TABLE).isPresent());
+    Assertions.assertFalse(cache.getIfPresent(ident5, 
Entity.EntityType.TABLE).isPresent());
+    Assertions.assertFalse(cache.getIfPresent(ident6, 
Entity.EntityType.TABLE).isPresent());
+  }
+
+  @Test
+  void testInvalidateSchema() {
+    EntityCache cache = new CaffeineEntityCache(new Config() {});
+
+    cache.put(entity1);
+    cache.put(entity2);
+    cache.put(entity3);
+    cache.put(entity4);
+    cache.put(entity5);
+    cache.put(entity6);
+    cache.put(entity7);
+
+    Assertions.assertEquals(7, cache.size());
+    Assertions.assertTrue(cache.contains(ident1, Entity.EntityType.SCHEMA));
+    Assertions.assertTrue(cache.contains(ident2, Entity.EntityType.SCHEMA));
+    Assertions.assertTrue(cache.contains(ident3, Entity.EntityType.TABLE));
+    Assertions.assertTrue(cache.contains(ident4, Entity.EntityType.TABLE));
+    Assertions.assertTrue(cache.contains(ident5, Entity.EntityType.TABLE));
+    Assertions.assertTrue(cache.contains(ident6, Entity.EntityType.CATALOG));
+    Assertions.assertTrue(cache.contains(ident7, Entity.EntityType.METALAKE));
+
+    cache.invalidate(ident1, Entity.EntityType.SCHEMA);
+
+    Assertions.assertEquals(5, cache.size());
+
+    Assertions.assertTrue(cache.getIfPresent(ident2, 
Entity.EntityType.SCHEMA).isPresent());
+    Assertions.assertTrue(cache.getIfPresent(ident4, 
Entity.EntityType.TABLE).isPresent());
+    Assertions.assertTrue(cache.getIfPresent(ident5, 
Entity.EntityType.TABLE).isPresent());
+    Assertions.assertTrue(cache.getIfPresent(ident6, 
Entity.EntityType.CATALOG).isPresent());
+    Assertions.assertTrue(cache.getIfPresent(ident7, 
Entity.EntityType.METALAKE).isPresent());
+
+    Assertions.assertFalse(cache.getIfPresent(ident1, 
Entity.EntityType.SCHEMA).isPresent());
+    Assertions.assertFalse(cache.getIfPresent(ident3, 
Entity.EntityType.TABLE).isPresent());
+  }
+
+  @Test
+  void testInvalidateTable() {
+    EntityCache cache = new CaffeineEntityCache(new Config() {});
+
+    cache.put(entity1);
+    cache.put(entity2);
+    cache.put(entity3);
+    cache.put(entity4);
+    cache.put(entity5);
+    cache.put(entity6);
+    cache.put(entity7);
+
+    Assertions.assertEquals(7, cache.size());
+    Assertions.assertTrue(cache.contains(ident1, Entity.EntityType.SCHEMA));
+    Assertions.assertTrue(cache.contains(ident2, Entity.EntityType.SCHEMA));
+    Assertions.assertTrue(cache.contains(ident3, Entity.EntityType.TABLE));
+    Assertions.assertTrue(cache.contains(ident4, Entity.EntityType.TABLE));
+    Assertions.assertTrue(cache.contains(ident5, Entity.EntityType.TABLE));
+    Assertions.assertTrue(cache.contains(ident6, Entity.EntityType.CATALOG));
+    Assertions.assertTrue(cache.contains(ident7, Entity.EntityType.METALAKE));
+
+    cache.invalidate(ident3, Entity.EntityType.TABLE);
+
+    Assertions.assertEquals(6, cache.size());
+    Assertions.assertTrue(cache.getIfPresent(ident1, 
Entity.EntityType.SCHEMA).isPresent());
+    Assertions.assertTrue(cache.getIfPresent(ident2, 
Entity.EntityType.SCHEMA).isPresent());
+    Assertions.assertTrue(cache.getIfPresent(ident4, 
Entity.EntityType.TABLE).isPresent());
+    Assertions.assertTrue(cache.getIfPresent(ident5, 
Entity.EntityType.TABLE).isPresent());
+    Assertions.assertTrue(cache.getIfPresent(ident6, 
Entity.EntityType.CATALOG).isPresent());
+    Assertions.assertTrue(cache.getIfPresent(ident7, 
Entity.EntityType.METALAKE).isPresent());
+
+    Assertions.assertFalse(cache.contains(ident3, Entity.EntityType.TABLE));
+  }
+
+  @Test
+  void testRemoveNonExistentEntity() {
+    EntityCache cache = new CaffeineEntityCache(new Config() {});
+
+    cache.put(entity1);
+
+    Assertions.assertEquals(1, cache.size());
+    Assertions.assertTrue(cache.contains(ident1, Entity.EntityType.SCHEMA));
+
+    Assertions.assertDoesNotThrow(() -> cache.invalidate(ident2, 
Entity.EntityType.SCHEMA));
+    Assertions.assertFalse(cache.invalidate(ident2, Entity.EntityType.SCHEMA));
+
+    Assertions.assertDoesNotThrow(() -> cache.invalidate(ident7, 
Entity.EntityType.METALAKE));
+    Assertions.assertFalse(cache.invalidate(ident7, 
Entity.EntityType.METALAKE));
+  }
+
+  @Test
+  void testExpireByTime() throws InterruptedException {
+    int waitTime = 3_000;
+    long expireTime = 1_000L;
+
+    Config config = new Config() {};
+    config.set(Configs.CACHE_EXPIRATION_TIME, expireTime);
+    config.set(Configs.CACHE_WEIGHER_ENABLED, false);
+    EntityCache cache = new CaffeineEntityCache(config);
+
+    cache.put(entity1);
+    Thread.sleep(waitTime);
+    cache.put(entity2);
+
+    Cache<EntityCacheKey, List<Entity>> caffeineObject =
+        (Cache<EntityCacheKey, List<Entity>>) getCacheDataFrom(cache);
+    caffeineObject.cleanUp();
+
+    Assertions.assertEquals(1, cache.size());
+    Assertions.assertFalse(cache.contains(entity1.nameIdentifier(), 
Entity.EntityType.SCHEMA));
+    Assertions.assertTrue(cache.contains(entity2.nameIdentifier(), 
Entity.EntityType.SCHEMA));
+  }
+
+  @Test
+  void testExpireByWeightExceedMaxWeight() {
+    Config config = new Config() {};
+    long oldMaxWeight = EntityCacheWeigher.MAX_WEIGHT;
+    EntityCacheWeigher.MAX_WEIGHT = 75;
+    config.set(Configs.CACHE_WEIGHER_ENABLED, true);
+    EntityCache cache = new CaffeineEntityCache(config);
+
+    cache.put(entity7);
+    Cache<EntityCacheKey, List<Entity>> caffeineObject =
+        (Cache<EntityCacheKey, List<Entity>>) getCacheDataFrom(cache);
+    caffeineObject.cleanUp();
+
+    Assertions.assertEquals(0, cache.size());
+    Assertions.assertFalse(cache.contains(entity7.nameIdentifier(), 
Entity.EntityType.METALAKE));
+
+    EntityCacheWeigher.MAX_WEIGHT = oldMaxWeight;
   }
 
-  // TODO Add other tests for cache
+  @Test
+  void testExpireByWeight() {
+    Config config = new Config() {};
+    long maxWeight = EntityCacheWeigher.MAX_WEIGHT;
+    EntityCacheWeigher.MAX_WEIGHT = 100;
+    config.set(Configs.CACHE_WEIGHER_ENABLED, true);
+    EntityCache cache = new CaffeineEntityCache(config);
+
+    cache.put(entity7);
+    Assertions.assertEquals(1, cache.size());
+    Assertions.assertTrue(cache.contains(entity7.nameIdentifier(), 
Entity.EntityType.METALAKE));
+
+    cache.put(entity1);
+    cache.put(entity2);
+    Cache<EntityCacheKey, List<Entity>> caffeineObject =
+        (Cache<EntityCacheKey, List<Entity>>) getCacheDataFrom(cache);
+    caffeineObject.cleanUp();
+
+    Assertions.assertEquals(2, cache.size());
+    Assertions.assertTrue(cache.contains(entity1.nameIdentifier(), 
Entity.EntityType.SCHEMA));
+    Assertions.assertTrue(cache.contains(entity2.nameIdentifier(), 
Entity.EntityType.SCHEMA));
 
-  private void initCache(Config config) {
-    real = new CaffeineEntityCache(config);
-    cache = spy(real);
+    EntityCacheWeigher.MAX_WEIGHT = maxWeight;
+  }
+
+  @Test
+  void testExpireBySize() {
+    Config config = new Config() {};
+    config.set(Configs.CACHE_WEIGHER_ENABLED, false);
+    config.set(Configs.CACHE_MAX_ENTRIES, 1);
+    EntityCache cache = new CaffeineEntityCache(config);
+
+    Cache<EntityCacheKey, List<Entity>> caffeineObject =
+        (Cache<EntityCacheKey, List<Entity>>) getCacheDataFrom(cache);
+
+    cache.put(entity1);
+    caffeineObject.cleanUp();
+    Assertions.assertEquals(1, cache.size());
+    Assertions.assertTrue(cache.contains(entity1.nameIdentifier(), 
Entity.EntityType.SCHEMA));
+
+    cache.put(entity2);
+    caffeineObject.cleanUp();
+    Assertions.assertEquals(1, cache.size());
+    Assertions.assertTrue(cache.contains(entity2.nameIdentifier(), 
Entity.EntityType.SCHEMA));
+
+    cache.put(entity3);
+    caffeineObject.cleanUp();
+    Assertions.assertEquals(1, cache.size());
+    Assertions.assertTrue(cache.contains(entity3.nameIdentifier(), 
Entity.EntityType.TABLE));
+  }
+
+  @Test
+  void testWeightCalculation() {
+    int metalakeWeight =
+        EntityCacheWeigher.getInstance()
+            .weigh(
+                EntityCacheKey.of(ident7, Entity.EntityType.METALAKE), 
ImmutableList.of(entity7));
+    Assertions.assertEquals(100, metalakeWeight);
+
+    int catalogWeight =
+        EntityCacheWeigher.getInstance()
+            .weigh(EntityCacheKey.of(ident6, Entity.EntityType.CATALOG), 
ImmutableList.of(entity6));
+    Assertions.assertEquals(75, catalogWeight);
+
+    int schemaWeight =
+        EntityCacheWeigher.getInstance()
+            .weigh(EntityCacheKey.of(ident1, Entity.EntityType.SCHEMA), 
ImmutableList.of(entity1));
+    Assertions.assertEquals(50, schemaWeight);
+
+    int tableWeight =
+        EntityCacheWeigher.getInstance()
+            .weigh(EntityCacheKey.of(ident3, Entity.EntityType.TABLE), 
ImmutableList.of(entity3));
+    Assertions.assertEquals(15, tableWeight);
+
+    int multiUserWeight =
+        EntityCacheWeigher.getInstance()
+            .weigh(
+                EntityCacheKey.of(
+                    ident12, Entity.EntityType.ROLE, 
SupportsRelationOperations.Type.ROLE_USER_REL),
+                ImmutableList.of(entity8, entity9));
+
+    Assertions.assertEquals(30, multiUserWeight);
+  }
+
+  @Test
+  void testGetIfPresentWithNull() {
+    EntityCache cache = new CaffeineEntityCache(new Config() {});
+    cache.put(entity1);
+
+    Assertions.assertThrows(
+        IllegalArgumentException.class, () -> cache.getIfPresent(null, 
Entity.EntityType.SCHEMA));
+    Assertions.assertThrows(IllegalArgumentException.class, () -> 
cache.getIfPresent(ident1, null));
+
+    Assertions.assertThrows(
+        IllegalArgumentException.class,
+        () -> cache.getIfPresent(null, ident12, Entity.EntityType.ROLE));
+    Assertions.assertThrows(
+        IllegalArgumentException.class,
+        () ->
+            cache.getIfPresent(
+                SupportsRelationOperations.Type.ROLE_USER_REL, null, 
Entity.EntityType.ROLE));
+    Assertions.assertThrows(
+        IllegalArgumentException.class,
+        () -> 
cache.getIfPresent(SupportsRelationOperations.Type.ROLE_USER_REL, ident12, 
null));
+    Assertions.assertThrows(
+        IllegalArgumentException.class,
+        () -> 
cache.getIfPresent(SupportsRelationOperations.Type.ROLE_USER_REL, ident12, 
null));
+  }
+
+  @Test
+  void testContainsWithNull() {
+    EntityCache cache = new CaffeineEntityCache(new Config() {});
+
+    Assertions.assertThrows(
+        IllegalArgumentException.class, () -> cache.contains(null, 
Entity.EntityType.SCHEMA));
+    Assertions.assertThrows(IllegalArgumentException.class, () -> 
cache.contains(ident7, null));
+
+    Assertions.assertThrows(
+        IllegalArgumentException.class,
+        () ->
+            cache.contains(
+                null, Entity.EntityType.ROLE, 
SupportsRelationOperations.Type.ROLE_USER_REL));
+    Assertions.assertThrows(
+        IllegalArgumentException.class,
+        () -> cache.contains(ident12, null, 
SupportsRelationOperations.Type.ROLE_USER_REL));
+    Assertions.assertThrows(
+        IllegalArgumentException.class,
+        () -> cache.contains(ident12, Entity.EntityType.ROLE, null));
+  }
+
+  @Test
+  void testInvalidateWithNull() {
+    EntityCache cache = new CaffeineEntityCache(new Config() {});
+
+    Assertions.assertThrows(
+        IllegalArgumentException.class, () -> cache.invalidate(null, 
Entity.EntityType.CATALOG));
+    Assertions.assertThrows(IllegalArgumentException.class, () -> 
cache.invalidate(ident7, null));
+
+    Assertions.assertThrows(
+        IllegalArgumentException.class,
+        () ->
+            cache.invalidate(
+                null, Entity.EntityType.ROLE, 
SupportsRelationOperations.Type.ROLE_USER_REL));
+    Assertions.assertThrows(
+        IllegalArgumentException.class,
+        () -> cache.invalidate(ident12, null, 
SupportsRelationOperations.Type.ROLE_USER_REL));
+    Assertions.assertThrows(
+        IllegalArgumentException.class,
+        () -> cache.invalidate(ident12, Entity.EntityType.ROLE, null));
+  }
+
+  @Test
+  void testPutWithNull() {
+    EntityCache cache = new CaffeineEntityCache(new Config() {});
+
+    Assertions.assertThrows(IllegalArgumentException.class, () -> 
cache.put(null));
+
+    Assertions.assertThrows(
+        IllegalArgumentException.class,
+        () ->
+            cache.put(
+                null,
+                Entity.EntityType.ROLE,
+                SupportsRelationOperations.Type.ROLE_USER_REL,
+                ImmutableList.of()));
+    Assertions.assertThrows(
+        IllegalArgumentException.class,
+        () ->
+            cache.put(
+                ident12, null, SupportsRelationOperations.Type.ROLE_USER_REL, 
ImmutableList.of()));
+    Assertions.assertThrows(
+        IllegalArgumentException.class,
+        () -> cache.put(ident12, Entity.EntityType.ROLE, null, 
ImmutableList.of()));
+    Assertions.assertThrows(
+        IllegalArgumentException.class,
+        () ->
+            cache.put(
+                ident12,
+                Entity.EntityType.ROLE,
+                SupportsRelationOperations.Type.ROLE_USER_REL,
+                null));
   }
 
   private void initTestNameIdentifier() {
@@ -361,9 +766,6 @@ public class TestCaffeineEntityCache {
 
     ident12 = NameIdentifierUtil.ofRole("metalake1", "role1");
     ident13 = NameIdentifierUtil.ofRole("metalake2", "role2");
-
-    // TODO remove next PR
-    System.out.println(ident8 + " " + ident9);
   }
 
   private void initTestEntities() {
diff --git 
a/core/src/test/java/org/apache/gravitino/storage/TestEntityStorage.java 
b/core/src/test/java/org/apache/gravitino/storage/TestEntityStorage.java
index 686c61a323..69914c91ed 100644
--- a/core/src/test/java/org/apache/gravitino/storage/TestEntityStorage.java
+++ b/core/src/test/java/org/apache/gravitino/storage/TestEntityStorage.java
@@ -146,6 +146,7 @@ public class TestEntityStorage {
     
Mockito.when(config.get(Configs.CACHE_EXPIRATION_TIME)).thenReturn(3_600_000L);
     Mockito.when(config.get(Configs.CACHE_WEIGHER_ENABLED)).thenReturn(true);
     Mockito.when(config.get(Configs.CACHE_STATS_ENABLED)).thenReturn(false);
+    
Mockito.when(config.get(Configs.CACHE_IMPLEMENTATION)).thenReturn("caffeine");
 
     BaseIT baseIT = new BaseIT();
 
diff --git a/core/src/test/java/org/apache/gravitino/tag/TestTagManager.java 
b/core/src/test/java/org/apache/gravitino/tag/TestTagManager.java
index 3506e1755e..5a7fc87d32 100644
--- a/core/src/test/java/org/apache/gravitino/tag/TestTagManager.java
+++ b/core/src/test/java/org/apache/gravitino/tag/TestTagManager.java
@@ -138,6 +138,7 @@ public class TestTagManager {
     
Mockito.when(config.get(Configs.CACHE_EXPIRATION_TIME)).thenReturn(3_600_000L);
     Mockito.when(config.get(Configs.CACHE_WEIGHER_ENABLED)).thenReturn(true);
     Mockito.when(config.get(Configs.CACHE_STATS_ENABLED)).thenReturn(false);
+    
Mockito.when(config.get(Configs.CACHE_IMPLEMENTATION)).thenReturn("caffeine");
 
     Mockito.doReturn(100000L).when(config).get(TREE_LOCK_MAX_NODE_IN_MEMORY);
     Mockito.doReturn(1000L).when(config).get(TREE_LOCK_MIN_NODE_IN_MEMORY);


Reply via email to