-
Notifications
You must be signed in to change notification settings - Fork 482
[#7177] feat(core): Add a Caffeine-based implementation for EntityCache #7330
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
[#7177] feat(core): Add a Caffeine-based implementation for EntityCache #7330
Conversation
Hi @xunliu @jerryshao , could you please review this PR when you have time? I’d really appreciate your feedback. |
core/src/main/java/org/apache/gravitino/cache/BaseEntityCache.java
Outdated
Show resolved
Hide resolved
core/src/main/java/org/apache/gravitino/cache/BaseEntityCache.java
Outdated
Show resolved
Hide resolved
NameIdentifier ident, Entity.EntityType type, SupportsRelationOperations.Type relType) | ||
NameIdentifier ident, | ||
Entity.EntityType type, | ||
SupportsRelationOperations.Type relType, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it necessary to have this parameter?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
core/src/main/java/org/apache/gravitino/cache/BaseEntityCache.java
Outdated
Show resolved
Hide resolved
if (config.get(Configs.CACHE_ENABLED)) { | ||
this.entityCache = CaffeineEntityCache.getInstance(config, entityStore); | ||
// TODO constructs a CachedEntityStore instance with the entityStore and entityCache | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As I remember correctly, the cache implementation is pluggable, so we should have a factory to create the cache interface. Here, we directly created a caffeine cache, I think we need to update the code.
Besides, should the cache be created after the entity store is initialized?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BTW, the entityCache
will be null if the cache is not enabled. From my point, we should not create a null
object deliberately, this will make it hard for the downstream developer to use this object, they have to check if this cache is null or not, and it is not a good implementation.
If the cache is not enabled, we should directly pass through to the entity store call. So for the users of this cache, they can directly use it without needing to add more check.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @jerryshao for the suggestion, makes sense!
I've updated the implementation to support pluggable cache via Java SPI. Specifically:
- Introduced a
CacheFactory
andCacheProvider
interface to support dynamic creation of EntityCache implementations; - Added a new configuration key
gravitino.cache.typeName
to specify the desired cache backend (e.g., "Caffeine"); - When cache is enabled, we instantiate the appropriate
EntityCache
via SPI and inject it into CachedEntityStore; - When cache is disabled, we still create a
CachedEntityStore
, but pass null for the cache instance, so all operations fall through to the underlying store transparently.
This way, downstream logic only needs to work withEntityStore
and doesn't need to care whether caching is enabled or not — the behavior is internally consistent and clean.
public boolean exists(NameIdentifier ident, Entity.EntityType entityType) throws IOException { | ||
return cache.withCacheLock( | ||
() -> { | ||
if (cache.contains(ident, entityType)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I saw you already have a same lock in cache, and you're using the lock again in here. So is it necessary to have a lock in the cache class, since you will always have the lock here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The internal contains(...)
call doesn’t need to acquire the lock again. I’ll remove the redundant withLock(...) to simplify the locking.
…ityCache fix some bugs in EntityCache and add test cases.
ceabb41
to
dac1b1e
Compare
Hi @jerryshao , I've completed the code updates and would appreciate your review of the PR when you have a moment. |
What changes were proposed in this pull request?
Fix some bugs in EntityCache and add test cases.
BaseEntityCache
for better reuse and abstraction.allFields
parameter toloadEntitiesByRelation
to align withSupportsRelationOperations#listEntitiesByRelation
.GravitinoEnv
for direct access to the cache from components.SupportsEntityStoreCache
andSupportsRelationEntityCache
interface inCaffeineEntityCache
.CaffeineEntityCache
and theCacheIndex
.testPutAndGet
,testGetIfPresent
testPutSameIdentifierEntities
testSize
testClear
testGetFromCache
testGetFromStore
testRemoveThenReload
testInvalidate*
seriestestRemoveNonExistentEntity
listEntitiesByRelation
from storetestGetFromStore
testExpireByTime
testExpireBySize
testExpireByWeight
testExpireByWeightExceedMaxWeight
testWeightCalculation
testLoadEntityThrowException
testNullEntityStore
test*WithNull
methodstestEquals
Why are the changes needed?
Fix: #7177
Does this PR introduce any user-facing change?
no
How was this patch tested?
local test.