27
27
28
28
import java .io .IOException ;
29
29
import java .util .ArrayList ;
30
- import java .util .Arrays ;
30
+ import java .util .Collections ;
31
31
import java .util .Iterator ;
32
+ import java .util .LinkedHashMap ;
32
33
import java .util .List ;
33
34
import java .util .Map ;
34
35
import java .util .NoSuchElementException ;
38
39
import java .util .function .Function ;
39
40
import java .util .function .Predicate ;
40
41
42
+ import static org .opensearch .cache .common .tier .TieredSpilloverCacheSettings .DISK_CACHE_ENABLED_SETTING_MAP ;
43
+
41
44
/**
42
45
* This cache spillover the evicted items from heap tier to disk tier. All the new items are first cached on heap
43
46
* and the items evicted from on heap cache are moved to disk based cache. If disk based cache also gets full,
@@ -67,20 +70,23 @@ public class TieredSpilloverCache<K, V> implements ICache<K, V> {
67
70
/**
68
71
* Maintains caching tiers in ascending order of cache latency.
69
72
*/
70
- private final List <ICache <K , V >> cacheList ;
73
+ private final Map <ICache <K , V >, Boolean > caches ;
71
74
private final List <Predicate <V >> policies ;
72
75
73
76
TieredSpilloverCache (Builder <K , V > builder ) {
74
77
Objects .requireNonNull (builder .onHeapCacheFactory , "onHeap cache builder can't be null" );
75
78
Objects .requireNonNull (builder .diskCacheFactory , "disk cache builder can't be null" );
79
+ Objects .requireNonNull (builder .cacheConfig , "cache config can't be null" );
80
+ Objects .requireNonNull (builder .cacheConfig .getClusterSettings (), "cluster settings can't be null" );
76
81
this .removalListener = Objects .requireNonNull (builder .removalListener , "Removal listener can't be null" );
77
82
78
83
this .onHeapCache = builder .onHeapCacheFactory .create (
79
84
new CacheConfig .Builder <K , V >().setRemovalListener (new RemovalListener <ICacheKey <K >, V >() {
80
85
@ Override
81
86
public void onRemoval (RemovalNotification <ICacheKey <K >, V > notification ) {
82
87
try (ReleasableLock ignore = writeLock .acquire ()) {
83
- if (SPILLOVER_REMOVAL_REASONS .contains (notification .getRemovalReason ())
88
+ if (caches .get (diskCache )
89
+ && SPILLOVER_REMOVAL_REASONS .contains (notification .getRemovalReason ())
84
90
&& evaluatePolicies (notification .getValue ())) {
85
91
diskCache .put (notification .getKey (), notification .getValue ());
86
92
} else {
@@ -103,9 +109,15 @@ && evaluatePolicies(notification.getValue())) {
103
109
104
110
);
105
111
this .diskCache = builder .diskCacheFactory .create (builder .cacheConfig , builder .cacheType , builder .cacheFactories );
106
- this .cacheList = Arrays .asList (onHeapCache , diskCache );
112
+ Boolean isDiskCacheEnabled = DISK_CACHE_ENABLED_SETTING_MAP .get (builder .cacheType ).get (builder .cacheConfig .getSettings ());
113
+ LinkedHashMap <ICache <K , V >, Boolean > cacheListMap = new LinkedHashMap <>();
114
+ cacheListMap .put (onHeapCache , true );
115
+ cacheListMap .put (diskCache , isDiskCacheEnabled );
116
+ this .caches = Collections .synchronizedMap (cacheListMap );
107
117
this .dimensionNames = builder .cacheConfig .getDimensionNames ();
108
118
this .policies = builder .policies ; // Will never be null; builder initializes it to an empty list
119
+ builder .cacheConfig .getClusterSettings ()
120
+ .addSettingsUpdateConsumer (DISK_CACHE_ENABLED_SETTING_MAP .get (builder .cacheType ), this ::enableDisableDiskCache );
109
121
}
110
122
111
123
// Package private for testing
@@ -118,6 +130,13 @@ ICache<K, V> getDiskCache() {
118
130
return diskCache ;
119
131
}
120
132
133
+ // Package private for testing.
134
+ void enableDisableDiskCache (Boolean isDiskCacheEnabled ) {
135
+ // When disk cache is disabled, we are not clearing up the disk cache entries yet as that should be part of
136
+ // separate cache/clear API.
137
+ this .caches .put (diskCache , isDiskCacheEnabled );
138
+ }
139
+
121
140
@ Override
122
141
public V get (ICacheKey <K > key ) {
123
142
return getValueFromTieredCache ().apply (key );
@@ -132,7 +151,6 @@ public void put(ICacheKey<K> key, V value) {
132
151
133
152
@ Override
134
153
public V computeIfAbsent (ICacheKey <K > key , LoadAwareCacheLoader <ICacheKey <K >, V > loader ) throws Exception {
135
-
136
154
V cacheValue = getValueFromTieredCache ().apply (key );
137
155
if (cacheValue == null ) {
138
156
// Add the value to the onHeap cache. We are calling computeIfAbsent which does another get inside.
@@ -151,19 +169,19 @@ public V computeIfAbsent(ICacheKey<K> key, LoadAwareCacheLoader<ICacheKey<K>, V>
151
169
public void invalidate (ICacheKey <K > key ) {
152
170
// We are trying to invalidate the key from all caches though it would be present in only of them.
153
171
// Doing this as we don't know where it is located. We could do a get from both and check that, but what will
154
- // also trigger a hit/miss listener event , so ignoring it for now.
172
+ // also count hits/misses stats , so ignoring it for now.
155
173
try (ReleasableLock ignore = writeLock .acquire ()) {
156
- for (ICache <K , V > cache : cacheList ) {
157
- cache .invalidate (key );
174
+ for (Map . Entry < ICache <K , V >, Boolean > cacheEntry : caches . entrySet () ) {
175
+ cacheEntry . getKey () .invalidate (key );
158
176
}
159
177
}
160
178
}
161
179
162
180
@ Override
163
181
public void invalidateAll () {
164
182
try (ReleasableLock ignore = writeLock .acquire ()) {
165
- for (ICache <K , V > cache : cacheList ) {
166
- cache .invalidateAll ();
183
+ for (Map . Entry < ICache <K , V >, Boolean > cacheEntry : caches . entrySet () ) {
184
+ cacheEntry . getKey () .invalidateAll ();
167
185
}
168
186
}
169
187
}
@@ -175,32 +193,39 @@ public void invalidateAll() {
175
193
@ SuppressWarnings ({ "unchecked" })
176
194
@ Override
177
195
public Iterable <ICacheKey <K >> keys () {
178
- Iterable <ICacheKey <K >>[] iterables = (Iterable <ICacheKey <K >>[]) new Iterable <?>[] { onHeapCache .keys (), diskCache .keys () };
179
- return new ConcatenatedIterables <ICacheKey <K >>(iterables );
196
+ List <Iterable <ICacheKey <K >>> iterableList = new ArrayList <>();
197
+ for (Map .Entry <ICache <K , V >, Boolean > cacheEntry : caches .entrySet ()) {
198
+ iterableList .add (cacheEntry .getKey ().keys ());
199
+ }
200
+ Iterable <ICacheKey <K >>[] iterables = (Iterable <ICacheKey <K >>[]) iterableList .toArray (new Iterable <?>[0 ]);
201
+ return new ConcatenatedIterables <>(iterables );
180
202
}
181
203
182
204
@ Override
183
205
public long count () {
184
206
long count = 0 ;
185
- for (ICache <K , V > cache : cacheList ) {
186
- count += cache .count ();
207
+ for (Map .Entry <ICache <K , V >, Boolean > cacheEntry : caches .entrySet ()) {
208
+ // Count for all the tiers irrespective of whether they are enabled or not. As eventually
209
+ // this will turn to zero once cache is cleared up either via invalidation or manually.
210
+ count += cacheEntry .getKey ().count ();
187
211
}
188
212
return count ;
189
213
}
190
214
191
215
@ Override
192
216
public void refresh () {
193
217
try (ReleasableLock ignore = writeLock .acquire ()) {
194
- for (ICache <K , V > cache : cacheList ) {
195
- cache .refresh ();
218
+ for (Map . Entry < ICache <K , V >, Boolean > cacheEntry : caches . entrySet () ) {
219
+ cacheEntry . getKey () .refresh ();
196
220
}
197
221
}
198
222
}
199
223
200
224
@ Override
201
225
public void close () throws IOException {
202
- for (ICache <K , V > cache : cacheList ) {
203
- cache .close ();
226
+ for (Map .Entry <ICache <K , V >, Boolean > cacheEntry : caches .entrySet ()) {
227
+ // Close all the caches here irrespective of whether they are enabled or not.
228
+ cacheEntry .getKey ().close ();
204
229
}
205
230
}
206
231
@@ -212,13 +237,12 @@ public ImmutableCacheStatsHolder stats() {
212
237
private Function <ICacheKey <K >, V > getValueFromTieredCache () {
213
238
return key -> {
214
239
try (ReleasableLock ignore = readLock .acquire ()) {
215
- for (ICache <K , V > cache : cacheList ) {
216
- V value = cache .get (key );
217
- if (value != null ) {
218
- // update hit stats
219
- return value ;
220
- } else {
221
- // update miss stats
240
+ for (Map .Entry <ICache <K , V >, Boolean > cacheEntry : caches .entrySet ()) {
241
+ if (cacheEntry .getValue ()) {
242
+ V value = cacheEntry .getKey ().get (key );
243
+ if (value != null ) {
244
+ return value ;
245
+ }
222
246
}
223
247
}
224
248
}
0 commit comments