Skip to content

Commit b31f1f5

Browse files
committed
some cleanup, added code comments
1 parent bad9981 commit b31f1f5

File tree

9 files changed

+190
-142
lines changed

9 files changed

+190
-142
lines changed

metadata-io/src/main/java/com/linkedin/metadata/search/client/CacheEvictionService.java

+38-23
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,16 @@
1111
import org.springframework.cache.Cache;
1212
import org.springframework.cache.CacheManager;
1313

14+
/**
15+
* A framework to enable search cache invalidation. The cache keys in the search cache are queries
16+
* of different forms and when an entity is modified, there isnt a simple direct correlation of that
17+
* entity to the queries in the cache (except for fully evaluating that search). This service
18+
* provides a mechanism to implement some a CacheKeyMatcher that implements some approximations to
19+
* check if a cache key is likely related to some entity that was updated and clear those entries.
20+
* The evict method can be called when entities are updated and it is important for those updates to
21+
* be visible in the UI without waiting for cache expiration. The eviction is disabled by default
22+
* and enabled via a spring application property searchService.enableEviction
23+
*/
1424
@RequiredArgsConstructor
1525
@Slf4j
1626
public class CacheEvictionService {
@@ -37,7 +47,9 @@ public void invalidate(String cacheName) {
3747
}
3848
}
3949

40-
public void evict(CacheKeyMatcher matcher){
50+
// Runs all cache keys through the supplied matcher implementation and clear the cache keys
51+
// identified by the matcher.
52+
public void evict(CacheKeyMatcher matcher) {
4153

4254
if (cachingEnabled && enableEviction) {
4355
Collection<String> cacheNames = cacheManager.getCacheNames();
@@ -50,28 +62,33 @@ public void evict(CacheKeyMatcher matcher){
5062
for (Object key : keys) {
5163
if (matcher.match(cacheName, key)) {
5264
cache.evict(key);
53-
evictCount ++;
65+
evictCount++;
5466
log.debug("From cache '{}' evicting key {}", cacheName, key);
5567
}
5668
}
57-
if (evictCount>0){
69+
if (evictCount > 0) {
5870
log.info("Evicted {} keys from cache {}", evictCount, cacheName);
5971
}
6072
}
6173
}
6274
}
6375
}
6476

77+
// Use a UrnCacheKeyMatcher implement to evict cache keys that are likely related to the supplied
78+
// list of urns
79+
public void evict(List<Urn> urns) {
80+
log.info("Attempting eviction of search cache due to updates to {}", urns);
81+
UrnCacheKeyMatcher matcher = new UrnCacheKeyMatcher(urns);
82+
evict(matcher);
83+
}
84+
6585
private Set<Object> getKeys(String cacheName) {
6686
// Enumerating cache keys is not part of the standard Cache interface, but needs is native cache
6787
// implementation
6888
// dependent and so must be implemented for all cache implementations we may use.
6989

7090
Cache springCache = cacheManager.getCache(cacheName);
71-
if (springCache == null) {
72-
return Collections.emptySet();
73-
}
74-
91+
assert (springCache != null);
7592
Object nativeCache = springCache.getNativeCache();
7693
if (nativeCache instanceof com.github.benmanes.caffeine.cache.Cache) {
7794
com.github.benmanes.caffeine.cache.Cache<Object, Object> caffeineCache =
@@ -86,22 +103,20 @@ private Set<Object> getKeys(String cacheName) {
86103
return Collections.emptySet();
87104
}
88105

89-
//Useful during matcher debugging, but voluminous
90-
private void dumpCache(String message){
106+
// Useful during matcher debugging, but voluminous
107+
void dumpCache(String message) {
91108
log.debug("Begin Dump {}", message);
92-
cacheManager.getCacheNames()
93-
.forEach(cacheName -> {
94-
log.debug("Dump cache: {}", cacheName);
95-
Cache cache = cacheManager.getCache(cacheName);
96-
getKeys(cacheName).forEach(key -> {
97-
log.debug(" key {} : {}", key, cache.get(key));
98-
});
99-
});
100-
}
101-
102-
public void evict(List<Urn> urns) {
103-
log.info("Attempting eviction of search cache due to updates to {}", urns);
104-
UrnCacheKeyMatcher matcher = new UrnCacheKeyMatcher(urns);
105-
evict(matcher);
109+
cacheManager
110+
.getCacheNames()
111+
.forEach(
112+
cacheName -> {
113+
log.debug("Dump cache: {}", cacheName);
114+
Cache cache = cacheManager.getCache(cacheName);
115+
getKeys(cacheName)
116+
.forEach(
117+
key -> {
118+
log.debug(" key {} : {}", key, cache.get(key));
119+
});
120+
});
106121
}
107122
}

metadata-io/src/main/java/com/linkedin/metadata/search/client/CachingEntitySearchService.java

+4-4
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,11 @@
3030

3131
@RequiredArgsConstructor
3232
public class CachingEntitySearchService {
33-
static final String ENTITY_SEARCH_SERVICE_SEARCH_CACHE_NAME = "entitySearchServiceSearch";
34-
static final String ENTITY_SEARCH_SERVICE_AUTOCOMPLETE_CACHE_NAME =
33+
public static final String ENTITY_SEARCH_SERVICE_SEARCH_CACHE_NAME = "entitySearchServiceSearch";
34+
public static final String ENTITY_SEARCH_SERVICE_AUTOCOMPLETE_CACHE_NAME =
3535
"entitySearchServiceAutoComplete";
36-
static final String ENTITY_SEARCH_SERVICE_BROWSE_CACHE_NAME = "entitySearchServiceBrowse";
37-
static final String ENTITY_SEARCH_SERVICE_SCROLL_CACHE_NAME = "entitySearchServiceScroll";
36+
public static final String ENTITY_SEARCH_SERVICE_BROWSE_CACHE_NAME = "entitySearchServiceBrowse";
37+
public static final String ENTITY_SEARCH_SERVICE_SCROLL_CACHE_NAME = "entitySearchServiceScroll";
3838

3939
private final CacheManager cacheManager;
4040
private final EntitySearchService

metadata-io/src/main/java/com/linkedin/metadata/search/client/UrnCacheKeyMatcher.java

+31-16
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,6 @@ public boolean supportsCache(String cacheName) {
3434

3535
@Override
3636
public boolean match(String cacheName, Object key) {
37-
if (!SUPPORTED_CACHE_NAMES.contains(cacheName)) {
38-
return false;
39-
}
40-
4137
switch (cacheName) {
4238
case ENTITY_SEARCH_SERVICE_SEARCH_CACHE_NAME:
4339
return matchSearchServiceCacheKey(key);
@@ -48,8 +44,9 @@ public boolean match(String cacheName, Object key) {
4844
}
4945

5046
private boolean matchSearchServiceScrollCacheKey(Object key) {
51-
Octet<?, List<String>, String, String, ?, ?, List<String>, ?> cacheKey
52-
= (Octet<?, List<String>, String, String, ?, ?, List<String>, ?>) key;
47+
Octet<?, List<String>, String, String, ?, ?, List<String>, ?> cacheKey =
48+
(Octet<?, List<String>, String, String, ?, ?, List<String>, ?>) key;
49+
// For reference - cache key contents
5350
// @Nonnull OperationContext opContext,
5451
// @Nonnull List<String> entities,
5552
// @Nonnull String query,
@@ -63,34 +60,52 @@ private boolean matchSearchServiceScrollCacheKey(Object key) {
6360
String query = (String) cacheKey.getValue(2);
6461
List<String> facets = (List<String>) cacheKey.getValue(6);
6562

66-
//Facets may contain urns. Since the check for urns in filters is similar, can append it to the filter.
67-
return isKeyImpactedByEntity(entitiesInCacheKey, query, filter + " " + String.join(" ",facets ));
63+
if (filter == null) {
64+
filter = "";
65+
}
66+
filter += " " + String.join(" ", facets);
67+
// Facets may contain urns. Since the check for urns in filters is similar, can append it to the
68+
// filter.
69+
return isKeyImpactedByEntity(entitiesInCacheKey, query, filter);
6870
}
6971

7072
private boolean matchSearchServiceCacheKey(Object key) {
7173
Septet<?, List<String>, ?, String, ?, ?, ?> cacheKey =
7274
(Septet<?, List<String>, ?, String, ?, ?, ?>) key;
75+
// For reference
76+
// @Nonnull OperationContext opContext,
77+
// @Nonnull List<String> entityNames,
78+
// @Nonnull String query,
79+
// @Nullable Filter filters,
80+
// List<SortCriterion> sortCriteria,
81+
// @Nonnull List<String> facets
82+
// querySize
7383

7484
List<String> entitiesInCacheKey = (List<String>) cacheKey.getValue(1);
7585
String filter = (String) cacheKey.getValue(3);
7686
String query = (String) cacheKey.getValue(2);
87+
List<String> facets = (List<String>) cacheKey.getValue(5);
88+
89+
// Facets may contain urns. Since the check for urns in filters is similar, can append it to the
90+
// filter.
91+
if (filter == null) {
92+
filter = "";
93+
}
94+
filter += " " + String.join(" ", facets);
7795

7896
return isKeyImpactedByEntity(entitiesInCacheKey, query, filter);
7997
}
8098

81-
boolean isKeyImpactedByEntity(List<String> entitiesInCacheKey, String query, String filter){
99+
boolean isKeyImpactedByEntity(List<String> entitiesInCacheKey, String query, String filter) {
82100
boolean entityMatch = entitiesInCacheKey.stream().anyMatch(entityTypes::contains);
83101
if (!entityMatch) {
84102
return false;
85103
}
86104

87-
if (filter == null){ //No filter, but already established there is an entity match
88-
return true;
89-
}
90-
91-
// Ignoring query for now. A query could make this cache entry more targeted, but till there is a quick way to
92-
// evaluate if the entities that were updated are affected by this query, ignoring it may mean some cache entries
93-
// are invalidated even if they may not be a match, and an uncached query result will still be fetched.
105+
// Ignoring query for now. A query could make this cache entry more targeted, but till there is
106+
// a quick way to evaluate if the entities that were updated are affected by this query,
107+
// ignoring it may mean some cache entries are invalidated even if they may not be a match,
108+
// and an uncached query result will still be fetched.
94109

95110
boolean containsUrn = filter.contains("urn:li");
96111
if (!containsUrn) {

0 commit comments

Comments
 (0)