diff --git a/extension/persistence/relational-jdbc/src/main/java/org/apache/polaris/extension/persistence/relational/jdbc/JdbcMetaStoreManagerFactory.java b/extension/persistence/relational-jdbc/src/main/java/org/apache/polaris/extension/persistence/relational/jdbc/JdbcMetaStoreManagerFactory.java index 07ac7f9725..93b163b189 100644 --- a/extension/persistence/relational-jdbc/src/main/java/org/apache/polaris/extension/persistence/relational/jdbc/JdbcMetaStoreManagerFactory.java +++ b/extension/persistence/relational-jdbc/src/main/java/org/apache/polaris/extension/persistence/relational/jdbc/JdbcMetaStoreManagerFactory.java @@ -43,6 +43,7 @@ import org.apache.polaris.core.persistence.PrincipalSecretsGenerator; import org.apache.polaris.core.persistence.bootstrap.RootCredentialsSet; import org.apache.polaris.core.persistence.cache.EntityCache; +import org.apache.polaris.core.persistence.cache.InMemoryEntityCache; import org.apache.polaris.core.persistence.dao.entity.BaseResult; import org.apache.polaris.core.persistence.dao.entity.EntityResult; import org.apache.polaris.core.persistence.dao.entity.PrincipalSecretsResult; @@ -204,7 +205,8 @@ public synchronized StorageCredentialCache getOrCreateStorageCredentialCache( public synchronized EntityCache getOrCreateEntityCache(RealmContext realmContext) { if (!entityCacheMap.containsKey(realmContext.getRealmIdentifier())) { PolarisMetaStoreManager metaStoreManager = getOrCreateMetaStoreManager(realmContext); - entityCacheMap.put(realmContext.getRealmIdentifier(), new EntityCache(metaStoreManager)); + entityCacheMap.put( + realmContext.getRealmIdentifier(), new InMemoryEntityCache(metaStoreManager)); } return entityCacheMap.get(realmContext.getRealmIdentifier()); diff --git a/polaris-core/src/main/java/org/apache/polaris/core/persistence/LocalPolarisMetaStoreManagerFactory.java b/polaris-core/src/main/java/org/apache/polaris/core/persistence/LocalPolarisMetaStoreManagerFactory.java index b005d411f5..85288f0dd5 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/persistence/LocalPolarisMetaStoreManagerFactory.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/persistence/LocalPolarisMetaStoreManagerFactory.java @@ -34,6 +34,7 @@ import org.apache.polaris.core.entity.PolarisEntityType; import org.apache.polaris.core.persistence.bootstrap.RootCredentialsSet; import org.apache.polaris.core.persistence.cache.EntityCache; +import org.apache.polaris.core.persistence.cache.InMemoryEntityCache; import org.apache.polaris.core.persistence.dao.entity.BaseResult; import org.apache.polaris.core.persistence.dao.entity.EntityResult; import org.apache.polaris.core.persistence.dao.entity.PrincipalSecretsResult; @@ -190,7 +191,8 @@ public synchronized StorageCredentialCache getOrCreateStorageCredentialCache( public synchronized EntityCache getOrCreateEntityCache(RealmContext realmContext) { if (!entityCacheMap.containsKey(realmContext.getRealmIdentifier())) { PolarisMetaStoreManager metaStoreManager = getOrCreateMetaStoreManager(realmContext); - entityCacheMap.put(realmContext.getRealmIdentifier(), new EntityCache(metaStoreManager)); + entityCacheMap.put( + realmContext.getRealmIdentifier(), new InMemoryEntityCache(metaStoreManager)); } return entityCacheMap.get(realmContext.getRealmIdentifier()); diff --git a/polaris-core/src/main/java/org/apache/polaris/core/persistence/cache/EntityCache.java b/polaris-core/src/main/java/org/apache/polaris/core/persistence/cache/EntityCache.java index 752342ffe6..cd438c9950 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/persistence/cache/EntityCache.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/persistence/cache/EntityCache.java @@ -18,235 +18,21 @@ */ package org.apache.polaris.core.persistence.cache; -import com.github.benmanes.caffeine.cache.Cache; -import com.github.benmanes.caffeine.cache.Caffeine; -import com.github.benmanes.caffeine.cache.RemovalListener; import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; -import java.util.AbstractMap; -import java.util.List; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.TimeUnit; import org.apache.polaris.core.PolarisCallContext; -import org.apache.polaris.core.config.BehaviorChangeConfiguration; -import org.apache.polaris.core.config.FeatureConfiguration; -import org.apache.polaris.core.config.PolarisConfiguration; import org.apache.polaris.core.entity.PolarisBaseEntity; import org.apache.polaris.core.entity.PolarisEntityType; -import org.apache.polaris.core.entity.PolarisGrantRecord; -import org.apache.polaris.core.persistence.PolarisMetaStoreManager; import org.apache.polaris.core.persistence.ResolvedPolarisEntity; -import org.apache.polaris.core.persistence.dao.entity.ResolvedEntityResult; - -/** The entity cache, can be private or shared */ -public class EntityCache { - - // cache mode - private EntityCacheMode cacheMode; - - // the meta store manager - private final PolarisMetaStoreManager polarisMetaStoreManager; - - // Caffeine cache to keep entries by id - private final Cache byId; - - // index by name - private final AbstractMap byName; - - /** - * Constructor. Cache can be private or shared - * - * @param polarisMetaStoreManager the meta store manager implementation - */ - public EntityCache(@Nonnull PolarisMetaStoreManager polarisMetaStoreManager) { - - // by name cache - this.byName = new ConcurrentHashMap<>(); - - // When an entry is removed, we simply remove it from the byName map - RemovalListener removalListener = - (key, value, cause) -> { - if (value != null) { - // compute name key - EntityCacheByNameKey nameKey = new EntityCacheByNameKey(value.getEntity()); - - // if it is still active, remove it from the name key - this.byName.remove(nameKey, value); - } - }; - - long weigherTarget = - PolarisConfiguration.loadConfig(FeatureConfiguration.ENTITY_CACHE_WEIGHER_TARGET); - Caffeine byIdBuilder = - Caffeine.newBuilder() - .maximumWeight(weigherTarget) - .weigher(EntityWeigher.asWeigher()) - .expireAfterAccess(1, TimeUnit.HOURS) // Expire entries after 1 hour of no access - .removalListener(removalListener); // Set the removal listener - - if (PolarisConfiguration.loadConfig(BehaviorChangeConfiguration.ENTITY_CACHE_SOFT_VALUES)) { - byIdBuilder.softValues(); - } - - // use a Caffeine cache to purge entries when those have not been used for a long time. - this.byId = byIdBuilder.build(); - - // remember the meta store manager - this.polarisMetaStoreManager = polarisMetaStoreManager; - - // enabled by default - this.cacheMode = EntityCacheMode.ENABLE; - } +/** Interface for a Polaris entity cache */ +public interface EntityCache { /** * Remove the specified cache entry from the cache * * @param cacheEntry cache entry to remove */ - public void removeCacheEntry(@Nonnull ResolvedPolarisEntity cacheEntry) { - // compute name key - EntityCacheByNameKey nameKey = new EntityCacheByNameKey(cacheEntry.getEntity()); - - // remove this old entry, this will immediately remove the named entry - this.byId.asMap().remove(cacheEntry.getEntity().getId(), cacheEntry); - - // remove it from the name key - this.byName.remove(nameKey, cacheEntry); - } - - /** - * Cache new entry - * - * @param cacheEntry new cache entry - */ - private void cacheNewEntry(@Nonnull ResolvedPolarisEntity cacheEntry) { - - // compute name key - EntityCacheByNameKey nameKey = new EntityCacheByNameKey(cacheEntry.getEntity()); - - // get old value if one exist - ResolvedPolarisEntity oldCacheEntry = this.byId.getIfPresent(cacheEntry.getEntity().getId()); - - // put new entry, only if really newer one - this.byId - .asMap() - .merge( - cacheEntry.getEntity().getId(), - cacheEntry, - (oldValue, newValue) -> this.isNewer(newValue, oldValue) ? newValue : oldValue); - - // only update the name key if this entity was not dropped - if (!cacheEntry.getEntity().isDropped()) { - // here we don't really care about concurrent update to the key. Basically if we are - // pointing to the wrong entry, we will detect this and fix the issue - this.byName.put(nameKey, cacheEntry); - } - - // remove old name if it has changed - if (oldCacheEntry != null) { - // old name - EntityCacheByNameKey oldNameKey = new EntityCacheByNameKey(oldCacheEntry.getEntity()); - if (!oldNameKey.equals(nameKey)) { - this.byName.remove(oldNameKey, oldCacheEntry); - } - } - } - - /** - * Determine if the newer value is really newer - * - * @param newValue new cache entry - * @param oldValue old cache entry - * @return true if the newer cache entry - */ - private boolean isNewer(ResolvedPolarisEntity newValue, ResolvedPolarisEntity oldValue) { - return (newValue.getEntity().getEntityVersion() > oldValue.getEntity().getEntityVersion() - || newValue.getEntity().getGrantRecordsVersion() - > oldValue.getEntity().getGrantRecordsVersion()); - } - - /** - * Replace an old entry with a new one - * - * @param oldCacheEntry old entry - * @param newCacheEntry new entry - */ - private void replaceCacheEntry( - @Nullable ResolvedPolarisEntity oldCacheEntry, @Nonnull ResolvedPolarisEntity newCacheEntry) { - - // need to remove old? - if (oldCacheEntry != null) { - // only replace if there is a difference - if (this.entityNameKeyMismatch(oldCacheEntry.getEntity(), newCacheEntry.getEntity()) - || oldCacheEntry.getEntity().getEntityVersion() - < newCacheEntry.getEntity().getEntityVersion() - || oldCacheEntry.getEntity().getGrantRecordsVersion() - < newCacheEntry.getEntity().getGrantRecordsVersion()) { - // write new one - this.cacheNewEntry(newCacheEntry); - - // delete the old one assuming it has not been replaced by the above new entry - this.removeCacheEntry(oldCacheEntry); - } - } else { - // write new one - this.cacheNewEntry(newCacheEntry); - } - } - - /** - * Check if two entities have different cache keys (either by id or by name) - * - * @param entity the entity - * @param otherEntity the other entity - * @return true if there is a mismatch - */ - private boolean entityNameKeyMismatch( - @Nonnull PolarisBaseEntity entity, @Nonnull PolarisBaseEntity otherEntity) { - return entity.getId() != otherEntity.getId() - || entity.getParentId() != otherEntity.getParentId() - || !entity.getName().equals(otherEntity.getName()) - || entity.getTypeCode() != otherEntity.getTypeCode(); - } - - /** - * Get the current cache mode - * - * @return the cache mode - */ - public EntityCacheMode getCacheMode() { - return cacheMode; - } - - /** - * Allows to change the caching mode for testing - * - * @param cacheMode the cache mode - */ - public void setCacheMode(EntityCacheMode cacheMode) { - this.cacheMode = cacheMode; - } - - /** - * Get a cache entity entry given the id of the entity - * - * @param entityId entity id - * @return the cache entry or null if not found - */ - public @Nullable ResolvedPolarisEntity getEntityById(long entityId) { - return byId.getIfPresent(entityId); - } - - /** - * Get a cache entity entry given the name key of the entity - * - * @param entityNameKey entity name key - * @return the cache entry or null if not found - */ - public @Nullable ResolvedPolarisEntity getEntityByName( - @Nonnull EntityCacheByNameKey entityNameKey) { - return byName.get(entityNameKey); - } + void removeCacheEntry(@Nonnull ResolvedPolarisEntity cacheEntry); /** * Refresh the cache if needs be with a version of the entity/grant records matching the minimum @@ -260,106 +46,12 @@ public void setCacheMode(EntityCacheMode cacheMode) { * records should be reloaded if needed * @return the cache entry for the entity or null if the specified entity does not exist */ - public @Nullable ResolvedPolarisEntity getAndRefreshIfNeeded( + @Nullable + ResolvedPolarisEntity getAndRefreshIfNeeded( @Nonnull PolarisCallContext callContext, @Nonnull PolarisBaseEntity entityToValidate, int entityMinVersion, - int entityGrantRecordsMinVersion) { - long entityCatalogId = entityToValidate.getCatalogId(); - long entityId = entityToValidate.getId(); - PolarisEntityType entityType = entityToValidate.getType(); - - // first lookup the cache to find the existing cache entry - ResolvedPolarisEntity existingCacheEntry = this.getEntityById(entityId); - - // the caller's fetched entity may have come from a stale lookup byName; we should consider - // the existingCacheEntry to be the older of the two for purposes of invalidation to make - // sure when we replaceCacheEntry we're also removing the old name if it's no longer valid - EntityCacheByNameKey nameKey = new EntityCacheByNameKey(entityToValidate); - ResolvedPolarisEntity existingCacheEntryByName = this.getEntityByName(nameKey); - if (existingCacheEntryByName != null - && existingCacheEntry != null - && isNewer(existingCacheEntry, existingCacheEntryByName)) { - existingCacheEntry = existingCacheEntryByName; - } - - // the new one to be returned - final ResolvedPolarisEntity newCacheEntry; - - // see if we need to load or refresh that entity - if (existingCacheEntry == null - || existingCacheEntry.getEntity().getEntityVersion() < entityMinVersion - || existingCacheEntry.getEntity().getGrantRecordsVersion() < entityGrantRecordsMinVersion) { - - // the refreshed entity - final ResolvedEntityResult refreshedCacheEntry; - - // was not found in the cache? - final PolarisBaseEntity entity; - final List grantRecords; - final int grantRecordsVersion; - if (existingCacheEntry == null) { - // try to load it - refreshedCacheEntry = - this.polarisMetaStoreManager.loadResolvedEntityById( - callContext, entityCatalogId, entityId, entityType); - if (refreshedCacheEntry.isSuccess()) { - entity = refreshedCacheEntry.getEntity(); - grantRecords = refreshedCacheEntry.getEntityGrantRecords(); - grantRecordsVersion = refreshedCacheEntry.getGrantRecordsVersion(); - } else { - return null; - } - } else { - // refresh it - refreshedCacheEntry = - this.polarisMetaStoreManager.refreshResolvedEntity( - callContext, - existingCacheEntry.getEntity().getEntityVersion(), - existingCacheEntry.getEntity().getGrantRecordsVersion(), - entityType, - entityCatalogId, - entityId); - if (refreshedCacheEntry.isSuccess()) { - entity = - (refreshedCacheEntry.getEntity() != null) - ? refreshedCacheEntry.getEntity() - : existingCacheEntry.getEntity(); - if (refreshedCacheEntry.getEntityGrantRecords() != null) { - grantRecords = refreshedCacheEntry.getEntityGrantRecords(); - grantRecordsVersion = refreshedCacheEntry.getGrantRecordsVersion(); - } else { - grantRecords = existingCacheEntry.getAllGrantRecords(); - grantRecordsVersion = existingCacheEntry.getEntity().getGrantRecordsVersion(); - } - } else { - // entity has been purged, remove it - this.removeCacheEntry(existingCacheEntry); - return null; - } - } - - // assert that entity, grant records and version are all set - callContext.getDiagServices().checkNotNull(entity, "unexpected_null_entity"); - callContext.getDiagServices().checkNotNull(grantRecords, "unexpected_null_grant_records"); - callContext - .getDiagServices() - .check(grantRecordsVersion > 0, "unexpected_null_grant_records_version"); - - // create new cache entry - newCacheEntry = - new ResolvedPolarisEntity( - callContext.getDiagServices(), entity, grantRecords, grantRecordsVersion); - - // insert cache entry - this.replaceCacheEntry(existingCacheEntry, newCacheEntry); - } else { - // found it in the cache and it is up-to-date, simply return it - newCacheEntry = existingCacheEntry; - } - - return newCacheEntry; - } + int entityGrantRecordsMinVersion); /** * Get the specified entity by name and load it if it is not found. @@ -370,52 +62,12 @@ && isNewer(existingCacheEntry, existingCacheEntryByName)) { * @return null if the entity does not exist or was dropped. Else return the entry for that * entity, either as found in the cache or loaded from the backend */ - public @Nullable EntityCacheLookupResult getOrLoadEntityById( + @Nullable + EntityCacheLookupResult getOrLoadEntityById( @Nonnull PolarisCallContext callContext, long entityCatalogId, long entityId, - PolarisEntityType entityType) { - - // if it exists, we are set - ResolvedPolarisEntity entry = this.getEntityById(entityId); - final boolean cacheHit; - - // we need to load it if it does not exist - if (entry == null) { - // this is a miss - cacheHit = false; - - // load it - ResolvedEntityResult result = - polarisMetaStoreManager.loadResolvedEntityById( - callContext, entityCatalogId, entityId, entityType); - - // not found, exit - if (!result.isSuccess()) { - return null; - } - - // if found, setup entry - callContext.getDiagServices().checkNotNull(result.getEntity(), "entity_should_loaded"); - callContext - .getDiagServices() - .checkNotNull(result.getEntityGrantRecords(), "entity_grant_records_should_loaded"); - entry = - new ResolvedPolarisEntity( - callContext.getDiagServices(), - result.getEntity(), - result.getEntityGrantRecords(), - result.getGrantRecordsVersion()); - - // the above loading could take a long time so check again if the entry exists and only - this.cacheNewEntry(entry); - } else { - cacheHit = true; - } - - // return what we found - return new EntityCacheLookupResult(entry, cacheHit); - } + PolarisEntityType entityType); /** * Get the specified entity by name and load it if it is not found. @@ -425,53 +77,7 @@ && isNewer(existingCacheEntry, existingCacheEntryByName)) { * @return null if the entity does not exist or was dropped. Else return the entry for that * entity, either as found in the cache or loaded from the backend */ - public @Nullable EntityCacheLookupResult getOrLoadEntityByName( - @Nonnull PolarisCallContext callContext, @Nonnull EntityCacheByNameKey entityNameKey) { - - // if it exists, we are set - ResolvedPolarisEntity entry = this.getEntityByName(entityNameKey); - final boolean cacheHit; - - // we need to load it if it does not exist - if (entry == null) { - // this is a miss - cacheHit = false; - - // load it - ResolvedEntityResult result = - polarisMetaStoreManager.loadResolvedEntityByName( - callContext, - entityNameKey.getCatalogId(), - entityNameKey.getParentId(), - entityNameKey.getType(), - entityNameKey.getName()); - - // not found, exit - if (!result.isSuccess()) { - return null; - } - - // validate return - callContext.getDiagServices().checkNotNull(result.getEntity(), "entity_should_loaded"); - callContext - .getDiagServices() - .checkNotNull(result.getEntityGrantRecords(), "entity_grant_records_should_loaded"); - - // if found, setup entry - entry = - new ResolvedPolarisEntity( - callContext.getDiagServices(), - result.getEntity(), - result.getEntityGrantRecords(), - result.getGrantRecordsVersion()); - - // the above loading could take a long time so check again if the entry exists and only - this.cacheNewEntry(entry); - } else { - cacheHit = true; - } - - // return what we found - return new EntityCacheLookupResult(entry, cacheHit); - } + @Nullable + EntityCacheLookupResult getOrLoadEntityByName( + @Nonnull PolarisCallContext callContext, @Nonnull EntityCacheByNameKey entityNameKey); } diff --git a/polaris-core/src/main/java/org/apache/polaris/core/persistence/cache/InMemoryEntityCache.java b/polaris-core/src/main/java/org/apache/polaris/core/persistence/cache/InMemoryEntityCache.java new file mode 100644 index 0000000000..4599f4aed1 --- /dev/null +++ b/polaris-core/src/main/java/org/apache/polaris/core/persistence/cache/InMemoryEntityCache.java @@ -0,0 +1,481 @@ +/* + * 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.polaris.core.persistence.cache; + +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; +import com.github.benmanes.caffeine.cache.RemovalListener; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; +import java.util.AbstractMap; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; +import org.apache.polaris.core.PolarisCallContext; +import org.apache.polaris.core.config.BehaviorChangeConfiguration; +import org.apache.polaris.core.config.FeatureConfiguration; +import org.apache.polaris.core.config.PolarisConfiguration; +import org.apache.polaris.core.entity.PolarisBaseEntity; +import org.apache.polaris.core.entity.PolarisEntityType; +import org.apache.polaris.core.entity.PolarisGrantRecord; +import org.apache.polaris.core.persistence.PolarisMetaStoreManager; +import org.apache.polaris.core.persistence.ResolvedPolarisEntity; +import org.apache.polaris.core.persistence.dao.entity.ResolvedEntityResult; + +/** An in-memory entity cache with a limit of 100k entities and a 1h TTL. */ +public class InMemoryEntityCache implements EntityCache { + + // cache mode + private EntityCacheMode cacheMode; + + // the meta store manager + private final PolarisMetaStoreManager polarisMetaStoreManager; + + // Caffeine cache to keep entries by id + private final Cache byId; + + // index by name + private final AbstractMap byName; + + /** + * Constructor. Cache can be private or shared + * + * @param polarisMetaStoreManager the meta store manager implementation + */ + public InMemoryEntityCache(@Nonnull PolarisMetaStoreManager polarisMetaStoreManager) { + + // by name cache + this.byName = new ConcurrentHashMap<>(); + + // When an entry is removed, we simply remove it from the byName map + RemovalListener removalListener = + (key, value, cause) -> { + if (value != null) { + // compute name key + EntityCacheByNameKey nameKey = new EntityCacheByNameKey(value.getEntity()); + + // if it is still active, remove it from the name key + this.byName.remove(nameKey, value); + } + }; + + long weigherTarget = + PolarisConfiguration.loadConfig(FeatureConfiguration.ENTITY_CACHE_WEIGHER_TARGET); + Caffeine byIdBuilder = + Caffeine.newBuilder() + .maximumWeight(weigherTarget) + .weigher(EntityWeigher.asWeigher()) + .expireAfterAccess(1, TimeUnit.HOURS) // Expire entries after 1 hour of no access + .removalListener(removalListener); // Set the removal listener + + if (PolarisConfiguration.loadConfig(BehaviorChangeConfiguration.ENTITY_CACHE_SOFT_VALUES)) { + byIdBuilder.softValues(); + } + + // use a Caffeine cache to purge entries when those have not been used for a long time. + this.byId = byIdBuilder.build(); + + // remember the meta store manager + this.polarisMetaStoreManager = polarisMetaStoreManager; + + // enabled by default + this.cacheMode = EntityCacheMode.ENABLE; + } + + /** + * Remove the specified cache entry from the cache + * + * @param cacheEntry cache entry to remove + */ + @Override + public void removeCacheEntry(@Nonnull ResolvedPolarisEntity cacheEntry) { + // compute name key + EntityCacheByNameKey nameKey = new EntityCacheByNameKey(cacheEntry.getEntity()); + + // remove this old entry, this will immediately remove the named entry + this.byId.asMap().remove(cacheEntry.getEntity().getId(), cacheEntry); + + // remove it from the name key + this.byName.remove(nameKey, cacheEntry); + } + + /** + * Cache new entry + * + * @param cacheEntry new cache entry + */ + private void cacheNewEntry(@Nonnull ResolvedPolarisEntity cacheEntry) { + + // compute name key + EntityCacheByNameKey nameKey = new EntityCacheByNameKey(cacheEntry.getEntity()); + + // get old value if one exist + ResolvedPolarisEntity oldCacheEntry = this.byId.getIfPresent(cacheEntry.getEntity().getId()); + + // put new entry, only if really newer one + this.byId + .asMap() + .merge( + cacheEntry.getEntity().getId(), + cacheEntry, + (oldValue, newValue) -> this.isNewer(newValue, oldValue) ? newValue : oldValue); + + // only update the name key if this entity was not dropped + if (!cacheEntry.getEntity().isDropped()) { + // here we don't really care about concurrent update to the key. Basically if we are + // pointing to the wrong entry, we will detect this and fix the issue + this.byName.put(nameKey, cacheEntry); + } + + // remove old name if it has changed + if (oldCacheEntry != null) { + // old name + EntityCacheByNameKey oldNameKey = new EntityCacheByNameKey(oldCacheEntry.getEntity()); + if (!oldNameKey.equals(nameKey)) { + this.byName.remove(oldNameKey, oldCacheEntry); + } + } + } + + /** + * Determine if the newer value is really newer + * + * @param newValue new cache entry + * @param oldValue old cache entry + * @return true if the newer cache entry + */ + private boolean isNewer(ResolvedPolarisEntity newValue, ResolvedPolarisEntity oldValue) { + return (newValue.getEntity().getEntityVersion() > oldValue.getEntity().getEntityVersion() + || newValue.getEntity().getGrantRecordsVersion() + > oldValue.getEntity().getGrantRecordsVersion()); + } + + /** + * Replace an old entry with a new one + * + * @param oldCacheEntry old entry + * @param newCacheEntry new entry + */ + private void replaceCacheEntry( + @Nullable ResolvedPolarisEntity oldCacheEntry, @Nonnull ResolvedPolarisEntity newCacheEntry) { + + // need to remove old? + if (oldCacheEntry != null) { + // only replace if there is a difference + if (this.entityNameKeyMismatch(oldCacheEntry.getEntity(), newCacheEntry.getEntity()) + || oldCacheEntry.getEntity().getEntityVersion() + < newCacheEntry.getEntity().getEntityVersion() + || oldCacheEntry.getEntity().getGrantRecordsVersion() + < newCacheEntry.getEntity().getGrantRecordsVersion()) { + // write new one + this.cacheNewEntry(newCacheEntry); + + // delete the old one assuming it has not been replaced by the above new entry + this.removeCacheEntry(oldCacheEntry); + } + } else { + // write new one + this.cacheNewEntry(newCacheEntry); + } + } + + /** + * Check if two entities have different cache keys (either by id or by name) + * + * @param entity the entity + * @param otherEntity the other entity + * @return true if there is a mismatch + */ + private boolean entityNameKeyMismatch( + @Nonnull PolarisBaseEntity entity, @Nonnull PolarisBaseEntity otherEntity) { + return entity.getId() != otherEntity.getId() + || entity.getParentId() != otherEntity.getParentId() + || !entity.getName().equals(otherEntity.getName()) + || entity.getTypeCode() != otherEntity.getTypeCode(); + } + + /** + * Get the current cache mode + * + * @return the cache mode + */ + public EntityCacheMode getCacheMode() { + return cacheMode; + } + + /** + * Allows to change the caching mode for testing + * + * @param cacheMode the cache mode + */ + public void setCacheMode(EntityCacheMode cacheMode) { + this.cacheMode = cacheMode; + } + + /** + * Get a cache entity entry given the id of the entity + * + * @param entityId entity id + * @return the cache entry or null if not found + */ + public @Nullable ResolvedPolarisEntity getEntityById(long entityId) { + return byId.getIfPresent(entityId); + } + + /** + * Get a cache entity entry given the name key of the entity + * + * @param entityNameKey entity name key + * @return the cache entry or null if not found + */ + public @Nullable ResolvedPolarisEntity getEntityByName( + @Nonnull EntityCacheByNameKey entityNameKey) { + return byName.get(entityNameKey); + } + + /** + * Refresh the cache if needs be with a version of the entity/grant records matching the minimum + * specified version. + * + * @param callContext the Polaris call context + * @param entityToValidate copy of the entity held by the caller to validate + * @param entityMinVersion minimum expected version. Should be reloaded if found in a cache with a + * version less than this one + * @param entityGrantRecordsMinVersion minimum grant records version which is expected, grants + * records should be reloaded if needed + * @return the cache entry for the entity or null if the specified entity does not exist + */ + @Override + public @Nullable ResolvedPolarisEntity getAndRefreshIfNeeded( + @Nonnull PolarisCallContext callContext, + @Nonnull PolarisBaseEntity entityToValidate, + int entityMinVersion, + int entityGrantRecordsMinVersion) { + long entityCatalogId = entityToValidate.getCatalogId(); + long entityId = entityToValidate.getId(); + PolarisEntityType entityType = entityToValidate.getType(); + + // first lookup the cache to find the existing cache entry + ResolvedPolarisEntity existingCacheEntry = this.getEntityById(entityId); + + // the caller's fetched entity may have come from a stale lookup byName; we should consider + // the existingCacheEntry to be the older of the two for purposes of invalidation to make + // sure when we replaceCacheEntry we're also removing the old name if it's no longer valid + EntityCacheByNameKey nameKey = new EntityCacheByNameKey(entityToValidate); + ResolvedPolarisEntity existingCacheEntryByName = this.getEntityByName(nameKey); + if (existingCacheEntryByName != null + && existingCacheEntry != null + && isNewer(existingCacheEntry, existingCacheEntryByName)) { + existingCacheEntry = existingCacheEntryByName; + } + + // the new one to be returned + final ResolvedPolarisEntity newCacheEntry; + + // see if we need to load or refresh that entity + if (existingCacheEntry == null + || existingCacheEntry.getEntity().getEntityVersion() < entityMinVersion + || existingCacheEntry.getEntity().getGrantRecordsVersion() < entityGrantRecordsMinVersion) { + + // the refreshed entity + final ResolvedEntityResult refreshedCacheEntry; + + // was not found in the cache? + final PolarisBaseEntity entity; + final List grantRecords; + final int grantRecordsVersion; + if (existingCacheEntry == null) { + // try to load it + refreshedCacheEntry = + this.polarisMetaStoreManager.loadResolvedEntityById( + callContext, entityCatalogId, entityId, entityType); + if (refreshedCacheEntry.isSuccess()) { + entity = refreshedCacheEntry.getEntity(); + grantRecords = refreshedCacheEntry.getEntityGrantRecords(); + grantRecordsVersion = refreshedCacheEntry.getGrantRecordsVersion(); + } else { + return null; + } + } else { + // refresh it + refreshedCacheEntry = + this.polarisMetaStoreManager.refreshResolvedEntity( + callContext, + existingCacheEntry.getEntity().getEntityVersion(), + existingCacheEntry.getEntity().getGrantRecordsVersion(), + entityType, + entityCatalogId, + entityId); + if (refreshedCacheEntry.isSuccess()) { + entity = + (refreshedCacheEntry.getEntity() != null) + ? refreshedCacheEntry.getEntity() + : existingCacheEntry.getEntity(); + if (refreshedCacheEntry.getEntityGrantRecords() != null) { + grantRecords = refreshedCacheEntry.getEntityGrantRecords(); + grantRecordsVersion = refreshedCacheEntry.getGrantRecordsVersion(); + } else { + grantRecords = existingCacheEntry.getAllGrantRecords(); + grantRecordsVersion = existingCacheEntry.getEntity().getGrantRecordsVersion(); + } + } else { + // entity has been purged, remove it + this.removeCacheEntry(existingCacheEntry); + return null; + } + } + + // assert that entity, grant records and version are all set + callContext.getDiagServices().checkNotNull(entity, "unexpected_null_entity"); + callContext.getDiagServices().checkNotNull(grantRecords, "unexpected_null_grant_records"); + callContext + .getDiagServices() + .check(grantRecordsVersion > 0, "unexpected_null_grant_records_version"); + + // create new cache entry + newCacheEntry = + new ResolvedPolarisEntity( + callContext.getDiagServices(), entity, grantRecords, grantRecordsVersion); + + // insert cache entry + this.replaceCacheEntry(existingCacheEntry, newCacheEntry); + } else { + // found it in the cache and it is up-to-date, simply return it + newCacheEntry = existingCacheEntry; + } + + return newCacheEntry; + } + + /** + * Get the specified entity by name and load it if it is not found. + * + * @param callContext the Polaris call context + * @param entityCatalogId id of the catalog where this entity resides or NULL_ID if top-level + * @param entityId id of the entity to lookup + * @return null if the entity does not exist or was dropped. Else return the entry for that + * entity, either as found in the cache or loaded from the backend + */ + @Override + public @Nullable EntityCacheLookupResult getOrLoadEntityById( + @Nonnull PolarisCallContext callContext, + long entityCatalogId, + long entityId, + PolarisEntityType entityType) { + + // if it exists, we are set + ResolvedPolarisEntity entry = this.getEntityById(entityId); + final boolean cacheHit; + + // we need to load it if it does not exist + if (entry == null) { + // this is a miss + cacheHit = false; + + // load it + ResolvedEntityResult result = + polarisMetaStoreManager.loadResolvedEntityById( + callContext, entityCatalogId, entityId, entityType); + + // not found, exit + if (!result.isSuccess()) { + return null; + } + + // if found, setup entry + callContext.getDiagServices().checkNotNull(result.getEntity(), "entity_should_loaded"); + callContext + .getDiagServices() + .checkNotNull(result.getEntityGrantRecords(), "entity_grant_records_should_loaded"); + entry = + new ResolvedPolarisEntity( + callContext.getDiagServices(), + result.getEntity(), + result.getEntityGrantRecords(), + result.getGrantRecordsVersion()); + + // the above loading could take a long time so check again if the entry exists and only + this.cacheNewEntry(entry); + } else { + cacheHit = true; + } + + // return what we found + return new EntityCacheLookupResult(entry, cacheHit); + } + + /** + * Get the specified entity by name and load it if it is not found. + * + * @param callContext the Polaris call context + * @param entityNameKey name of the entity to load + * @return null if the entity does not exist or was dropped. Else return the entry for that + * entity, either as found in the cache or loaded from the backend + */ + @Override + public @Nullable EntityCacheLookupResult getOrLoadEntityByName( + @Nonnull PolarisCallContext callContext, @Nonnull EntityCacheByNameKey entityNameKey) { + + // if it exists, we are set + ResolvedPolarisEntity entry = this.getEntityByName(entityNameKey); + final boolean cacheHit; + + // we need to load it if it does not exist + if (entry == null) { + // this is a miss + cacheHit = false; + + // load it + ResolvedEntityResult result = + polarisMetaStoreManager.loadResolvedEntityByName( + callContext, + entityNameKey.getCatalogId(), + entityNameKey.getParentId(), + entityNameKey.getType(), + entityNameKey.getName()); + + // not found, exit + if (!result.isSuccess()) { + return null; + } + + // validate return + callContext.getDiagServices().checkNotNull(result.getEntity(), "entity_should_loaded"); + callContext + .getDiagServices() + .checkNotNull(result.getEntityGrantRecords(), "entity_grant_records_should_loaded"); + + // if found, setup entry + entry = + new ResolvedPolarisEntity( + callContext.getDiagServices(), + result.getEntity(), + result.getEntityGrantRecords(), + result.getGrantRecordsVersion()); + + // the above loading could take a long time so check again if the entry exists and only + this.cacheNewEntry(entry); + } else { + cacheHit = true; + } + + // return what we found + return new EntityCacheLookupResult(entry, cacheHit); + } +} diff --git a/polaris-core/src/test/java/org/apache/polaris/core/persistence/cache/EntityCacheTest.java b/polaris-core/src/test/java/org/apache/polaris/core/persistence/cache/InMemoryEntityCacheTest.java similarity index 98% rename from polaris-core/src/test/java/org/apache/polaris/core/persistence/cache/EntityCacheTest.java rename to polaris-core/src/test/java/org/apache/polaris/core/persistence/cache/InMemoryEntityCacheTest.java index c21bef0024..72b75ad05b 100644 --- a/polaris-core/src/test/java/org/apache/polaris/core/persistence/cache/EntityCacheTest.java +++ b/polaris-core/src/test/java/org/apache/polaris/core/persistence/cache/InMemoryEntityCacheTest.java @@ -45,7 +45,7 @@ import org.mockito.Mockito; /** Unit testing of the entity cache */ -public class EntityCacheTest { +public class InMemoryEntityCacheTest { // diag services private final PolarisDiagnostics diagServices; @@ -87,7 +87,7 @@ public class EntityCacheTest { * - P2(PR2) * */ - public EntityCacheTest() { + public InMemoryEntityCacheTest() { diagServices = new PolarisDefaultDiagServiceImpl(); store = new TreeMapMetaStore(diagServices); metaStore = new TreeMapTransactionalPersistenceImpl(store, Mockito.mock(), RANDOM_SECRETS); @@ -102,14 +102,14 @@ public EntityCacheTest() { /** * @return new cache for the entity store */ - EntityCache allocateNewCache() { - return new EntityCache(this.metaStoreManager); + InMemoryEntityCache allocateNewCache() { + return new InMemoryEntityCache(this.metaStoreManager); } @Test void testGetOrLoadEntityByName() { // get a new cache - EntityCache cache = this.allocateNewCache(); + InMemoryEntityCache cache = this.allocateNewCache(); // should exist and no cache hit EntityCacheLookupResult lookup = @@ -278,7 +278,7 @@ void testGetOrLoadEntityByName() { @Test void testRefresh() { // allocate a new cache - EntityCache cache = this.allocateNewCache(); + InMemoryEntityCache cache = this.allocateNewCache(); // should exist and no cache hit EntityCacheLookupResult lookup = @@ -423,7 +423,7 @@ void testRefresh() { @Test void testRenameAndCacheDestinationBeforeLoadingSource() { // get a new cache - EntityCache cache = this.allocateNewCache(); + InMemoryEntityCache cache = this.allocateNewCache(); EntityCacheLookupResult lookup = cache.getOrLoadEntityByName( diff --git a/polaris-core/src/testFixtures/java/org/apache/polaris/core/persistence/BaseResolverTest.java b/polaris-core/src/testFixtures/java/org/apache/polaris/core/persistence/BaseResolverTest.java index 108db5a6d7..728187a38d 100644 --- a/polaris-core/src/testFixtures/java/org/apache/polaris/core/persistence/BaseResolverTest.java +++ b/polaris-core/src/testFixtures/java/org/apache/polaris/core/persistence/BaseResolverTest.java @@ -40,7 +40,7 @@ import org.apache.polaris.core.entity.PolarisPrivilege; import org.apache.polaris.core.entity.PrincipalEntity; import org.apache.polaris.core.entity.PrincipalRoleEntity; -import org.apache.polaris.core.persistence.cache.EntityCache; +import org.apache.polaris.core.persistence.cache.InMemoryEntityCache; import org.apache.polaris.core.persistence.dao.entity.EntityResult; import org.apache.polaris.core.persistence.dao.entity.ResolvedEntityResult; import org.apache.polaris.core.persistence.resolver.Resolver; @@ -57,7 +57,7 @@ public abstract class BaseResolverTest { protected PolarisBaseEntity P1; // cache we are using - protected EntityCache cache; + protected InMemoryEntityCache cache; // whenever constructing a new Resolver instance, if false, disable cache for that Resolver // instance by giving it a null cache regardless of the current state of the test-level @@ -433,7 +433,7 @@ private Resolver allocateResolver(@Nullable String referenceCatalogName) { * @return new resolver to test with */ @Nonnull - private Resolver allocateResolver(@Nullable EntityCache cache) { + private Resolver allocateResolver(@Nullable InMemoryEntityCache cache) { return this.allocateResolver(cache, null); } @@ -447,7 +447,7 @@ private Resolver allocateResolver(@Nullable EntityCache cache) { */ @Nonnull private Resolver allocateResolver( - @Nullable EntityCache cache, @Nullable String referenceCatalogName) { + @Nullable InMemoryEntityCache cache, @Nullable String referenceCatalogName) { return this.allocateResolver(cache, null, referenceCatalogName); } @@ -462,13 +462,13 @@ private Resolver allocateResolver( */ @Nonnull private Resolver allocateResolver( - @Nullable EntityCache cache, + @Nullable InMemoryEntityCache cache, Set principalRolesScope, @Nullable String referenceCatalogName) { // create a new cache if needs be if (cache == null) { - this.cache = new EntityCache(metaStoreManager()); + this.cache = new InMemoryEntityCache(metaStoreManager()); } boolean allRoles = principalRolesScope == null; Optional> roleEntities = @@ -531,7 +531,7 @@ public String getAuthenticationScheme() { * @param principalRoleName name of the principal role, should exist */ private void resolvePrincipalAndPrincipalRole( - EntityCache cache, String principalName, boolean exists, String principalRoleName) { + InMemoryEntityCache cache, String principalName, boolean exists, String principalRoleName) { Resolver resolver = allocateResolver(cache); // for a principal creation, we simply want to test if the principal we are creating exists @@ -600,7 +600,7 @@ private void resolvePrincipalAndPrincipalRole( * @return resolver we created and which has been validated. */ private Resolver resolveDriver( - EntityCache cache, + InMemoryEntityCache cache, Set principalRolesScope, String principalName, boolean isPrincipalNameOptional, @@ -631,7 +631,7 @@ private Resolver resolveDriver( * @return resolver we created and which has been validated. */ private Resolver resolveDriver( - EntityCache cache, + InMemoryEntityCache cache, String catalogName, ResolverPath path, List paths, @@ -651,7 +651,7 @@ private Resolver resolveDriver( * @return resolver we created and which has been validated. */ private Resolver resolveDriver( - EntityCache cache, + InMemoryEntityCache cache, Set principalRolesScope, String catalogName, Set expectedActivatedCatalogRoles) { @@ -679,7 +679,7 @@ private Resolver resolveDriver( * @return resolver we created and which has been validated. */ private Resolver resolveDriver( - EntityCache cache, + InMemoryEntityCache cache, String catalogName, String catalogRoleName, ResolverStatus.StatusEnum expectedStatus) { @@ -715,7 +715,7 @@ private Resolver resolveDriver( * @return resolver we created and which has been validated. */ private Resolver resolveDriver( - EntityCache cache, + InMemoryEntityCache cache, Set principalRolesScope, String principalName, boolean isPrincipalNameOptional, diff --git a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/CatalogNoEntityCacheTest.java b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/CatalogNoEntityCacheTest.java index 7e92b97b5a..895b6ab81e 100644 --- a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/CatalogNoEntityCacheTest.java +++ b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/CatalogNoEntityCacheTest.java @@ -22,7 +22,7 @@ import io.quarkus.test.junit.TestProfile; import jakarta.annotation.Nullable; import org.apache.polaris.core.persistence.PolarisMetaStoreManager; -import org.apache.polaris.core.persistence.cache.EntityCache; +import org.apache.polaris.core.persistence.cache.InMemoryEntityCache; @QuarkusTest @TestProfile(IcebergCatalogTest.Profile.class) @@ -30,7 +30,7 @@ public class CatalogNoEntityCacheTest extends IcebergCatalogTest { @Nullable @Override - protected EntityCache createEntityCache(PolarisMetaStoreManager metaStoreManager) { + protected InMemoryEntityCache createEntityCache(PolarisMetaStoreManager metaStoreManager) { return null; } } diff --git a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/GenericTableCatalogTest.java b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/GenericTableCatalogTest.java index 462cc7bb73..96b1010b43 100644 --- a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/GenericTableCatalogTest.java +++ b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/GenericTableCatalogTest.java @@ -63,7 +63,7 @@ import org.apache.polaris.core.persistence.PolarisEntityManager; import org.apache.polaris.core.persistence.PolarisMetaStoreManager; import org.apache.polaris.core.persistence.bootstrap.RootCredentialsSet; -import org.apache.polaris.core.persistence.cache.EntityCache; +import org.apache.polaris.core.persistence.cache.InMemoryEntityCache; import org.apache.polaris.core.persistence.dao.entity.BaseResult; import org.apache.polaris.core.persistence.dao.entity.PrincipalSecretsResult; import org.apache.polaris.core.persistence.transactional.TransactionalPersistence; @@ -171,7 +171,9 @@ public void before(TestInfo testInfo) { Clock.systemDefaultZone()); entityManager = new PolarisEntityManager( - metaStoreManager, new StorageCredentialCache(), new EntityCache(metaStoreManager)); + metaStoreManager, + new StorageCredentialCache(), + new InMemoryEntityCache(metaStoreManager)); callContext = CallContext.of(realmContext, polarisContext); @@ -293,8 +295,8 @@ public StorageCredentialCache getOrCreateStorageCredentialCache(RealmContext rea } @Override - public EntityCache getOrCreateEntityCache(RealmContext realmContext) { - return new EntityCache(metaStoreManager); + public InMemoryEntityCache getOrCreateEntityCache(RealmContext realmContext) { + return new InMemoryEntityCache(metaStoreManager); } @Override diff --git a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/IcebergCatalogTest.java b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/IcebergCatalogTest.java index 210a792e2b..3be818274f 100644 --- a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/IcebergCatalogTest.java +++ b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/IcebergCatalogTest.java @@ -94,7 +94,7 @@ import org.apache.polaris.core.persistence.PolarisMetaStoreManager; import org.apache.polaris.core.persistence.PolarisResolvedPathWrapper; import org.apache.polaris.core.persistence.bootstrap.RootCredentialsSet; -import org.apache.polaris.core.persistence.cache.EntityCache; +import org.apache.polaris.core.persistence.cache.InMemoryEntityCache; import org.apache.polaris.core.persistence.dao.entity.BaseResult; import org.apache.polaris.core.persistence.dao.entity.EntityResult; import org.apache.polaris.core.persistence.dao.entity.PrincipalSecretsResult; @@ -199,7 +199,8 @@ public static void setUpMocks() { } @Nullable - protected abstract EntityCache createEntityCache(PolarisMetaStoreManager metaStoreManager); + protected abstract InMemoryEntityCache createEntityCache( + PolarisMetaStoreManager metaStoreManager); @BeforeEach @SuppressWarnings("unchecked") @@ -380,8 +381,8 @@ public StorageCredentialCache getOrCreateStorageCredentialCache(RealmContext rea } @Override - public EntityCache getOrCreateEntityCache(RealmContext realmContext) { - return new EntityCache(metaStoreManager); + public InMemoryEntityCache getOrCreateEntityCache(RealmContext realmContext) { + return new InMemoryEntityCache(metaStoreManager); } @Override diff --git a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/IcebergCatalogViewTest.java b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/IcebergCatalogViewTest.java index 8d7708dd73..45f138e485 100644 --- a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/IcebergCatalogViewTest.java +++ b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/IcebergCatalogViewTest.java @@ -57,7 +57,7 @@ import org.apache.polaris.core.persistence.MetaStoreManagerFactory; import org.apache.polaris.core.persistence.PolarisEntityManager; import org.apache.polaris.core.persistence.PolarisMetaStoreManager; -import org.apache.polaris.core.persistence.cache.EntityCache; +import org.apache.polaris.core.persistence.cache.InMemoryEntityCache; import org.apache.polaris.core.secrets.UserSecretsManager; import org.apache.polaris.core.secrets.UserSecretsManagerFactory; import org.apache.polaris.core.storage.cache.StorageCredentialCache; @@ -145,7 +145,9 @@ public void before(TestInfo testInfo) { PolarisEntityManager entityManager = new PolarisEntityManager( - metaStoreManager, new StorageCredentialCache(), new EntityCache(metaStoreManager)); + metaStoreManager, + new StorageCredentialCache(), + new InMemoryEntityCache(metaStoreManager)); CallContext callContext = CallContext.of(realmContext, polarisContext); CallContext.setCurrentContext(callContext); diff --git a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/PolarisCatalogWithEntityCacheTest.java b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/PolarisCatalogWithEntityCacheTest.java index 64c96bb5be..079b02befc 100644 --- a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/PolarisCatalogWithEntityCacheTest.java +++ b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/PolarisCatalogWithEntityCacheTest.java @@ -22,7 +22,7 @@ import io.quarkus.test.junit.TestProfile; import jakarta.annotation.Nullable; import org.apache.polaris.core.persistence.PolarisMetaStoreManager; -import org.apache.polaris.core.persistence.cache.EntityCache; +import org.apache.polaris.core.persistence.cache.InMemoryEntityCache; @QuarkusTest @TestProfile(IcebergCatalogTest.Profile.class) @@ -30,7 +30,7 @@ public class PolarisCatalogWithEntityCacheTest extends IcebergCatalogTest { @Nullable @Override - protected EntityCache createEntityCache(PolarisMetaStoreManager metaStoreManager) { - return new EntityCache(metaStoreManager); + protected InMemoryEntityCache createEntityCache(PolarisMetaStoreManager metaStoreManager) { + return new InMemoryEntityCache(metaStoreManager); } } diff --git a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/PolicyCatalogTest.java b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/PolicyCatalogTest.java index 966d564571..57bd521324 100644 --- a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/PolicyCatalogTest.java +++ b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/PolicyCatalogTest.java @@ -71,7 +71,7 @@ import org.apache.polaris.core.persistence.PolarisMetaStoreManager; import org.apache.polaris.core.persistence.PolicyMappingAlreadyExistsException; import org.apache.polaris.core.persistence.bootstrap.RootCredentialsSet; -import org.apache.polaris.core.persistence.cache.EntityCache; +import org.apache.polaris.core.persistence.cache.InMemoryEntityCache; import org.apache.polaris.core.persistence.dao.entity.BaseResult; import org.apache.polaris.core.persistence.dao.entity.PrincipalSecretsResult; import org.apache.polaris.core.persistence.transactional.TransactionalPersistence; @@ -194,7 +194,9 @@ public void before(TestInfo testInfo) { Clock.systemDefaultZone()); entityManager = new PolarisEntityManager( - metaStoreManager, new StorageCredentialCache(), new EntityCache(metaStoreManager)); + metaStoreManager, + new StorageCredentialCache(), + new InMemoryEntityCache(metaStoreManager)); callContext = CallContext.of(realmContext, polarisContext); @@ -315,8 +317,8 @@ public StorageCredentialCache getOrCreateStorageCredentialCache(RealmContext rea } @Override - public EntityCache getOrCreateEntityCache(RealmContext realmContext) { - return new EntityCache(metaStoreManager); + public InMemoryEntityCache getOrCreateEntityCache(RealmContext realmContext) { + return new InMemoryEntityCache(metaStoreManager); } @Override