3939import org .elasticsearch .core .TimeValue ;
4040import org .elasticsearch .env .Environment ;
4141import org .elasticsearch .env .NodeEnvironment ;
42+ import org .elasticsearch .index .shard .ShardId ;
4243import org .elasticsearch .index .store .LuceneFilesExtensions ;
4344import org .elasticsearch .monitor .fs .FsProbe ;
4445import org .elasticsearch .node .NodeRoleSettings ;
7879/**
7980 * A caching layer on a local node to minimize network roundtrips to the remote blob store.
8081 */
81- public class SharedBlobCacheService <KeyType > implements Releasable {
82+ public class SharedBlobCacheService <KeyType extends SharedBlobCacheService .KeyBase > implements Releasable {
83+
84+ public interface KeyBase {
85+ ShardId shardId ();
86+ }
8287
8388 private static final String SHARED_CACHE_SETTINGS_PREFIX = "xpack.searchable.snapshot.shared_cache." ;
8489
@@ -301,6 +306,8 @@ private interface Cache<K, T> extends Releasable {
301306 int forceEvict (Predicate <K > cacheKeyPredicate );
302307
303308 void forceEvictAsync (Predicate <K > cacheKey );
309+
310+ int forceEvict (ShardId shard , Predicate <K > cacheKeyPredicate );
304311 }
305312
306313 private abstract static class CacheEntry <T > {
@@ -759,7 +766,7 @@ public Stats getStats() {
759766 }
760767
761768 public void removeFromCache (KeyType cacheKey ) {
762- forceEvict (cacheKey ::equals );
769+ forceEvict (cacheKey . shardId (), cacheKey ::equals );
763770 }
764771
765772 /**
@@ -770,7 +777,10 @@ public void removeFromCache(KeyType cacheKey) {
770777 */
771778 public int forceEvict (Predicate <KeyType > cacheKeyPredicate ) {
772779 return cache .forceEvict (cacheKeyPredicate );
780+ }
773781
782+ public int forceEvict (ShardId shard , Predicate <KeyType > cacheKeyPredicate ) {
783+ return cache .forceEvict (shard , cacheKeyPredicate );
774784 }
775785
776786 /**
@@ -867,7 +877,7 @@ protected boolean assertOffsetsWithinFileLength(long offset, long length, long f
867877 * always be used, ensuring the right ordering between incRef/tryIncRef and ensureOpen
868878 * (see {@link SharedBlobCacheService.LFUCache#maybeEvictAndTakeForFrequency(Runnable, int)})
869879 */
870- static class CacheFileRegion <KeyType > extends EvictableRefCounted {
880+ static class CacheFileRegion <KeyType extends KeyBase > extends EvictableRefCounted {
871881
872882 private static final VarHandle VH_IO = findIOVarHandle ();
873883
@@ -1634,7 +1644,7 @@ void touch() {
16341644 }
16351645 }
16361646
1637- private final ConcurrentHashMap < RegionKey <KeyType >, LFUCacheEntry > keyMapping = new ConcurrentHashMap <>();
1647+ private final KeyMapping < ShardId , RegionKey <KeyType >, LFUCacheEntry > keyMapping = new KeyMapping <>();
16381648 private final LFUCacheEntry [] freqs ;
16391649 private final int maxFreq ;
16401650 private final DecayAndNewEpochTask decayAndNewEpochTask ;
@@ -1653,8 +1663,9 @@ public void close() {
16531663 decayAndNewEpochTask .close ();
16541664 }
16551665
1666+ // used by tests
16561667 int getFreq (CacheFileRegion <KeyType > cacheFileRegion ) {
1657- return keyMapping .get (cacheFileRegion .regionKey ).freq ;
1668+ return keyMapping .get (cacheFileRegion .regionKey . file (). shardId (), cacheFileRegion . regionKey ).freq ;
16581669 }
16591670
16601671 @ Override
@@ -1663,10 +1674,11 @@ public LFUCacheEntry get(KeyType cacheKey, long fileLength, int region) {
16631674 final long now = epoch .get ();
16641675 // try to just get from the map on the fast-path to save instantiating the capturing lambda needed on the slow path
16651676 // if we did not find an entry
1666- var entry = keyMapping .get (regionKey );
1677+ var entry = keyMapping .get (cacheKey . shardId (), regionKey );
16671678 if (entry == null ) {
16681679 final int effectiveRegionSize = computeCacheFileRegionSize (fileLength , region );
16691680 entry = keyMapping .computeIfAbsent (
1681+ cacheKey .shardId (),
16701682 regionKey ,
16711683 key -> new LFUCacheEntry (new CacheFileRegion <KeyType >(SharedBlobCacheService .this , key , effectiveRegionSize ), now )
16721684 );
@@ -1706,7 +1718,7 @@ public int forceEvict(Predicate<KeyType> cacheKeyPredicate) {
17061718 boolean evicted = entry .chunk .forceEvict ();
17071719 if (evicted && entry .chunk .volatileIO () != null ) {
17081720 unlink (entry );
1709- keyMapping .remove (entry .chunk .regionKey , entry );
1721+ keyMapping .remove (entry .chunk .regionKey . file . shardId (), entry . chunk . regionKey , entry );
17101722 evictedCount ++;
17111723 if (frequency > 0 ) {
17121724 nonZeroFrequencyEvictedCount ++;
@@ -1719,6 +1731,10 @@ public int forceEvict(Predicate<KeyType> cacheKeyPredicate) {
17191731 return evictedCount ;
17201732 }
17211733
1734+ private boolean removeKeyMappingForEntry (LFUCacheEntry entry ) {
1735+ return keyMapping .remove (entry .chunk .regionKey .file ().shardId (), entry .chunk .regionKey , entry );
1736+ }
1737+
17221738 @ Override
17231739 public void forceEvictAsync (Predicate <KeyType > cacheKeyPredicate ) {
17241740 asyncEvictionsRunner .enqueueTask (new ActionListener <>() {
@@ -1739,10 +1755,42 @@ public void onFailure(Exception e) {
17391755 });
17401756 }
17411757
1758+ @ Override
1759+ public int forceEvict (ShardId shard , Predicate <KeyType > cacheKeyPredicate ) {
1760+ final List <LFUCacheEntry > matchingEntries = new ArrayList <>();
1761+ keyMapping .forEach (shard , (key , entry ) -> {
1762+ if (cacheKeyPredicate .test (key .file )) {
1763+ matchingEntries .add (entry );
1764+ }
1765+ });
1766+
1767+ var evictedCount = 0 ;
1768+ var nonZeroFrequencyEvictedCount = 0 ;
1769+ if (matchingEntries .isEmpty () == false ) {
1770+ synchronized (SharedBlobCacheService .this ) {
1771+ for (LFUCacheEntry entry : matchingEntries ) {
1772+ int frequency = entry .freq ;
1773+ boolean evicted = entry .chunk .forceEvict ();
1774+ if (evicted && entry .chunk .volatileIO () != null ) {
1775+ unlink (entry );
1776+ assert shard .equals (entry .chunk .regionKey .file .shardId ());
1777+ keyMapping .remove (shard , entry .chunk .regionKey , entry );
1778+ evictedCount ++;
1779+ if (frequency > 0 ) {
1780+ nonZeroFrequencyEvictedCount ++;
1781+ }
1782+ }
1783+ }
1784+ }
1785+ }
1786+ blobCacheMetrics .getEvictedCountNonZeroFrequency ().incrementBy (nonZeroFrequencyEvictedCount );
1787+ return evictedCount ;
1788+ }
1789+
17421790 private LFUCacheEntry initChunk (LFUCacheEntry entry ) {
17431791 assert Thread .holdsLock (entry .chunk );
17441792 RegionKey <KeyType > regionKey = entry .chunk .regionKey ;
1745- if (keyMapping .get (regionKey ) != entry ) {
1793+ if (keyMapping .get (regionKey . file (). shardId (), regionKey ) != entry ) {
17461794 throwAlreadyClosed ("no free region found (contender)" );
17471795 }
17481796 // new item
@@ -1765,7 +1813,7 @@ private LFUCacheEntry initChunk(LFUCacheEntry entry) {
17651813 if (io != null ) {
17661814 assignToSlot (entry , io );
17671815 } else {
1768- boolean removed = keyMapping . remove ( regionKey , entry );
1816+ boolean removed = removeKeyMappingForEntry ( entry );
17691817 assert removed ;
17701818 throwAlreadyClosed ("no free region found" );
17711819 }
@@ -1780,7 +1828,7 @@ private void assignToSlot(LFUCacheEntry entry, SharedBytes.IO freeSlot) {
17801828 if (entry .chunk .isEvicted ()) {
17811829 assert regionOwners .remove (freeSlot ) == entry .chunk ;
17821830 freeRegions .add (freeSlot );
1783- keyMapping . remove ( entry . chunk . regionKey , entry );
1831+ removeKeyMappingForEntry ( entry );
17841832 throwAlreadyClosed ("evicted during free region allocation" );
17851833 }
17861834 pushEntryToBack (entry );
@@ -1985,7 +2033,7 @@ private SharedBytes.IO maybeEvictAndTakeForFrequency(Runnable evictedNotificatio
19852033 }
19862034 } finally {
19872035 unlink (entry );
1988- keyMapping . remove ( entry . chunk . regionKey , entry );
2036+ removeKeyMappingForEntry ( entry );
19892037 }
19902038 }
19912039 } finally {
@@ -2020,7 +2068,7 @@ public boolean maybeEvictLeastUsed() {
20202068 boolean evicted = entry .chunk .tryEvict ();
20212069 if (evicted && entry .chunk .volatileIO () != null ) {
20222070 unlink (entry );
2023- keyMapping . remove ( entry . chunk . regionKey , entry );
2071+ removeKeyMappingForEntry ( entry );
20242072 return true ;
20252073 }
20262074 }
0 commit comments