View Javadoc

1   package com.atlassian.vcache.internal.test;
2   
3   import com.atlassian.vcache.DirectExternalCache;
4   import com.atlassian.vcache.PutPolicy;
5   import com.atlassian.vcache.TransactionalExternalCache;
6   import com.atlassian.vcache.internal.LongMetric;
7   import com.atlassian.vcache.internal.MetricLabel;
8   import com.atlassian.vcache.internal.RequestMetrics;
9   import com.google.common.collect.Maps;
10  import org.junit.Rule;
11  import org.junit.Test;
12  import org.slf4j.Logger;
13  import org.slf4j.LoggerFactory;
14  
15  import java.util.Map;
16  import java.util.Optional;
17  import java.util.concurrent.CompletionStage;
18  import java.util.stream.Collectors;
19  
20  import static com.atlassian.vcache.VCacheUtils.unsafeJoin;
21  import static com.atlassian.vcache.internal.test.CacheMetricsMatcher.hasMetric;
22  import static com.atlassian.vcache.internal.test.CacheMetricsMatcher.hasSize;
23  import static com.atlassian.vcache.internal.test.CompletionStageSuccessful.successful;
24  import static com.atlassian.vcache.internal.test.CompletionStageSuccessful.successfulWith;
25  import static org.hamcrest.Matchers.containsInAnyOrder;
26  import static org.hamcrest.Matchers.greaterThan;
27  import static org.hamcrest.Matchers.is;
28  import static org.hamcrest.Matchers.not;
29  import static org.junit.Assert.assertThat;
30  
31  /**
32   * Base test class for the {@link com.atlassian.vcache.TransactionalExternalCache}.
33   */
34  public abstract class AbstractTransactionalExternalCacheIT {
35      protected static final String CACHE_NAME = "fragileSting";
36      private static final Logger log = LoggerFactory.getLogger(AbstractTransactionalExternalCacheIT.class);
37  
38      @Rule
39      public LoggingTestWatcher watcher = new LoggingTestWatcher(log);
40  
41      protected abstract TransactionalExternalCache<String> cache();
42  
43      protected abstract DirectExternalCache<String> directCache();
44  
45      protected abstract void directCacheRefresh();
46  
47      protected abstract void cacheTransactionSync();
48      protected abstract void cacheTransactionDiscard();
49      protected abstract RequestMetrics requestMetrics();
50  
51      @Test
52      public void existing_getSupplier_getSupplier() {
53          final CompletionStage<Boolean> put = directCache().put("josie", "football", PutPolicy.PUT_ALWAYS);
54  
55          assertThat(put, successfulWith(is(true)));
56  
57          final CompletionStage<String> get1 = cache().get("josie", () -> "soccer");
58  
59          assertThat(get1, successfulWith(is("football")));
60  
61          final CompletionStage<String> get2 = cache().get("josie", () -> "soccer");
62  
63          assertThat(get2, successfulWith(is("football")));
64  
65          final Map<MetricLabel, ? extends LongMetric> cacheMetrics =
66                  requestMetrics().allExternalCacheLongMetrics().get(CACHE_NAME);
67          assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_GET_CALL, is(2L), greaterThan(0L)));
68          assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_HITS, is(2L), is(2L)));
69          assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_REMOTE_GET, is(1L), is(1L)));
70          assertThat(cacheMetrics, hasSize(is(3)));
71      }
72  
73      @Test
74      public void put_get_sync_successful2() {
75          cache().put("claira", "dancing", PutPolicy.PUT_ALWAYS);
76  
77          final CompletionStage<Optional<String>> get1 = cache().get("claira");
78  
79          assertThat(get1, successfulWith(is(Optional.of("dancing"))));
80  
81          // Now use the directCache to verify no data has been written
82          directCacheRefresh();
83          final CompletionStage<Optional<String>> get2 = directCache().get("claira");
84  
85          assertThat(get2, successfulWith(is(Optional.empty())));
86  
87          // Now sync the changes
88          cacheTransactionSync();
89  
90          // And check the changes are now visible
91          directCacheRefresh();
92          final CompletionStage<Optional<String>> get3 = directCache().get("claira");
93  
94          assertThat(get3, successfulWith(is(Optional.of("dancing"))));
95  
96          // Now check another lookup will succeed with the transactional cache
97          final CompletionStage<Optional<String>> get4 = cache().get("claira");
98  
99          assertThat(get4, successfulWith(is(Optional.of("dancing"))));
100 
101         final Map<MetricLabel, ? extends LongMetric> cacheMetrics =
102                 requestMetrics().allExternalCacheLongMetrics().get(CACHE_NAME);
103         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_GET_CALL, is(2L), greaterThan(0L)));
104         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_PUT_CALL, is(1L), greaterThan(0L)));
105         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_HITS, is(2L), is(2L)));
106         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_REMOTE_GET, is(1L), is(1L)));
107         assertThat(cacheMetrics, hasSize(is(4)));
108     }
109 
110     @Test
111     public void put_get_sync_unsuccessful2() throws Exception {
112         cache().put("claira", "dancing", PutPolicy.ADD_ONLY); // Must be ADD_ONLY
113 
114         final CompletionStage<Optional<String>> get1 = cache().get("claira");
115 
116         assertThat(get1, successfulWith(is(Optional.of("dancing"))));
117 
118         // Now use the directCache to verify no data has been written
119         directCacheRefresh();
120         final CompletionStage<Optional<String>> get2 = directCache().get("claira");
121 
122         assertThat(get2, successfulWith(is(Optional.empty())));
123 
124         // Now add values to the backing cache using the directCache
125         final CompletionStage<Map<String, String>> get3 =
126                 directCache().getBulk(keys -> Maps.asMap(keys, k -> k + "-1"), "claira", "josephine");
127 
128         assertThat(get3, successful());
129         assertThat(unsafeJoin(get3).keySet(), containsInAnyOrder("claira", "josephine"));
130         assertThat(unsafeJoin(get3).values(), containsInAnyOrder("claira-1", "josephine-1"));
131 
132         // Now sync the changes
133         cacheTransactionSync();
134 
135         // And check the cache is empty
136         directCacheRefresh();
137         final CompletionStage<Map<String, Optional<String>>> get4 = directCache().getBulk("claira", "josephine");
138 
139         assertThat(get4, successful());
140         assertThat(unsafeJoin(get4).keySet(), containsInAnyOrder("claira", "josephine"));
141         assertThat(unsafeJoin(get4).values(), containsInAnyOrder(Optional.empty(), Optional.empty()));
142 
143         final Map<MetricLabel, ? extends LongMetric> cacheMetrics =
144                 requestMetrics().allExternalCacheLongMetrics().get(CACHE_NAME);
145         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_GET_CALL, is(1L), greaterThan(0L)));
146         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_PUT_CALL, is(1L), greaterThan(0L)));
147         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_HITS, is(1L), is(1L)));
148         assertThat(cacheMetrics, hasSize(is(3)));
149     }
150 
151     @Test
152     public void getBulk_getBulkFunction_directPut_sync() throws Exception {
153         final CompletionStage<Boolean> put1 = directCache().put("claira", "dancing", PutPolicy.PUT_ALWAYS);
154 
155         assertThat(put1, successful());
156 
157         final CompletionStage<Map<String, Optional<String>>> get1 = cache().getBulk("claira", "josephine");
158 
159         assertThat(get1, successful());
160         assertThat(unsafeJoin(get1).keySet(), containsInAnyOrder("claira", "josephine"));
161         assertThat(unsafeJoin(get1).values(), containsInAnyOrder(Optional.of("dancing"), Optional.empty()));
162 
163         final CompletionStage<Map<String, String>> get2 =
164                 cache().getBulk(keys -> Maps.asMap(keys, k -> "football"), "claira", "josephine");
165 
166         assertThat(get2, successful());
167         assertThat(unsafeJoin(get2).keySet(), containsInAnyOrder("claira", "josephine"));
168         assertThat(unsafeJoin(get2).values(), containsInAnyOrder("dancing", "football"));
169 
170         // Now add an entry to the backend cache for "josephine" and verify one does not exist yet
171         directCacheRefresh();
172         final CompletionStage<Boolean> put2 = directCache().put("josephine", "soccer", PutPolicy.ADD_ONLY);
173 
174         assertThat(put2, successfulWith(is(true)));
175 
176         cacheTransactionSync();
177 
178         // Now verify nothing in the cache, due to panic when added josephine
179         directCacheRefresh();
180         final CompletionStage<Map<String, Optional<String>>> get3 = directCache().getBulk("claira", "josephine");
181 
182         assertThat(get3, successful());
183         assertThat(unsafeJoin(get3).keySet(), containsInAnyOrder("claira", "josephine"));
184         assertThat(unsafeJoin(get3).values(), containsInAnyOrder(Optional.empty(), Optional.empty()));
185 
186         final Map<MetricLabel, ? extends LongMetric> cacheMetrics =
187                 requestMetrics().allExternalCacheLongMetrics().get(CACHE_NAME);
188         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_GET_CALL, is(2L), greaterThan(0L)));
189         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_FACTORY_CALL, is(1L), greaterThan(0L)));
190         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_FACTORY_KEYS, is(1L), is(1L)));
191         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_HITS, is(2L), is(2L)));
192         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_MISSES, is(2L), is(2L)));
193         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_REMOTE_GET, is(1L), is(1L)));
194         assertThat(cacheMetrics, hasSize(is(6)));
195     }
196 
197     @Test
198     public void put_get_remove_get() {
199         cache().put("nirvana", "shotgun", PutPolicy.PUT_ALWAYS);
200 
201         final CompletionStage<Optional<String>> get1 = cache().get("nirvana");
202 
203         assertThat(get1, successfulWith(is(Optional.of("shotgun"))));
204 
205         cache().remove("a", "b", "nirvana");
206 
207         final CompletionStage<Optional<String>> get2 = cache().get("nirvana");
208 
209         assertThat(get2, successfulWith(is(Optional.empty())));
210 
211         final Map<MetricLabel, ? extends LongMetric> cacheMetrics =
212                 requestMetrics().allExternalCacheLongMetrics().get(CACHE_NAME);
213         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_GET_CALL, is(2L), greaterThan(0L)));
214         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_PUT_CALL, is(1L), greaterThan(0L)));
215         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_REMOVE_CALL, is(1L), greaterThan(0L)));
216         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_HITS, is(1L), is(1L)));
217         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_MISSES, is(1L), is(1L)));
218         assertThat(cacheMetrics, hasSize(is(5)));
219     }
220 
221     @Test
222     public void getSupplier_removeAll_get() {
223         final CompletionStage<String> get1 = cache().get("vienna", () -> "sandwiches");
224 
225         assertThat(get1, successfulWith(is("sandwiches")));
226 
227         cache().removeAll();
228 
229         final CompletionStage<Optional<String>> get2 = cache().get("vienna");
230 
231         assertThat(get2, successfulWith(is(Optional.empty())));
232 
233         final Map<MetricLabel, ? extends LongMetric> cacheMetrics =
234                 requestMetrics().allExternalCacheLongMetrics().get(CACHE_NAME);
235         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_GET_CALL, is(2L), greaterThan(0L)));
236         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_REMOVE_ALL_CALL, is(1L), greaterThan(0L)));
237         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_SUPPLIER_CALL, is(1L), greaterThan(0L)));
238         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_MISSES, is(2L), is(2L)));
239         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_REMOTE_GET, is(1L), is(1L)));
240         assertThat(cacheMetrics, hasSize(is(5)));
241     }
242 
243     @Test
244     public void getSupplier_removeAll_getSupplier_getSupplier() {
245         final CompletionStage<String> get1 = cache().get("vienna", () -> "sandwiches");
246 
247         assertThat(get1, successfulWith(is("sandwiches")));
248 
249         cache().removeAll();
250 
251         final CompletionStage<Optional<String>> get2 = cache().get("vienna");
252 
253         assertThat(get2, successfulWith(is(Optional.empty())));
254 
255         final CompletionStage<String> get3 = cache().get("vienna", () -> "new value");
256 
257         assertThat(get3, successfulWith(is("new value")));
258 
259         final CompletionStage<String> get4 = cache().get("vienna", () -> "not used");
260 
261         assertThat(get4, successfulWith(is("new value")));
262 
263         final Map<MetricLabel, ? extends LongMetric> cacheMetrics =
264                 requestMetrics().allExternalCacheLongMetrics().get(CACHE_NAME);
265         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_GET_CALL, is(4L), greaterThan(0L)));
266         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_REMOVE_ALL_CALL, is(1L), greaterThan(0L)));
267         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_SUPPLIER_CALL, is(2L), greaterThan(0L)));
268         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_HITS, is(1L), is(1L)));
269         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_MISSES, is(3L), is(3L)));
270         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_REMOTE_GET, is(1L), is(1L)));
271         assertThat(cacheMetrics, hasSize(is(6)));
272     }
273 
274     @Test
275     public void getSupplier_removeAll_getBulk_getBulk() {
276         final CompletionStage<String> get1 = cache().get("vienna", () -> "sandwiches");
277 
278         assertThat(get1, successfulWith(is("sandwiches")));
279 
280         cache().removeAll();
281 
282         final CompletionStage<Optional<String>> get2 = cache().get("vienna");
283 
284         assertThat(get2, successfulWith(is(Optional.empty())));
285 
286         final CompletionStage<Map<String, String>> get3 = cache().getBulk(keys -> Maps.asMap(keys, k -> k + "-1"), "vienna");
287 
288         assertThat(get3, successful());
289         assertThat(unsafeJoin(get3).keySet(), containsInAnyOrder("vienna"));
290         assertThat(unsafeJoin(get3).values(), containsInAnyOrder("vienna-1"));
291 
292         final CompletionStage<Map<String, String>> get4 = cache().getBulk(keys -> Maps.asMap(keys, k -> k + "-2"), "vienna");
293 
294         assertThat(get4, successful());
295         assertThat(unsafeJoin(get4).keySet(), containsInAnyOrder("vienna"));
296         assertThat(unsafeJoin(get4).values(), containsInAnyOrder("vienna-1"));
297 
298         final Map<MetricLabel, ? extends LongMetric> cacheMetrics =
299                 requestMetrics().allExternalCacheLongMetrics().get(CACHE_NAME);
300         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_GET_CALL, is(4L), greaterThan(0L)));
301         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_FACTORY_CALL, is(1L), greaterThan(0L)));
302         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_REMOVE_ALL_CALL, is(1L), greaterThan(0L)));
303         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_SUPPLIER_CALL, is(1L), greaterThan(0L)));
304         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_FACTORY_KEYS, is(1L), is(1L)));
305         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_HITS, is(1L), is(1L)));
306         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_MISSES, is(3L), is(3L)));
307         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_REMOTE_GET, is(1L), is(1L)));
308         assertThat(cacheMetrics, hasSize(is(8)));
309     }
310 
311     @Test
312     public void put_get_discard() {
313         cache().put("josie", "surfing", PutPolicy.PUT_ALWAYS);
314 
315         final CompletionStage<Optional<String>> get1 = cache().get("josie");
316 
317         assertThat(get1, successfulWith(is(Optional.of("surfing"))));
318 
319         // Now use the directCache to verify no data has been written
320         directCacheRefresh();
321         final CompletionStage<Optional<String>> get2 = directCache().get("josie");
322 
323         assertThat(get2, successfulWith(is(Optional.empty())));
324 
325         // Now discard the changes
326         cacheTransactionDiscard();
327 
328         // And check the changes are not visible
329         directCacheRefresh();
330         final CompletionStage<Optional<String>> get3 = directCache().get("josie");
331 
332         assertThat(get3, successfulWith(is(Optional.empty())));
333 
334         final Map<MetricLabel, ? extends LongMetric> cacheMetrics =
335                 requestMetrics().allExternalCacheLongMetrics().get(CACHE_NAME);
336         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_GET_CALL, is(1L), greaterThan(0L)));
337         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_PUT_CALL, is(1L), greaterThan(0L)));
338         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_HITS, is(1L), is(1L)));
339         assertThat(cacheMetrics, hasSize(is(3)));
340     }
341 
342     @Test
343     public void existing_remove_get() {
344         directCache().put("key", "existing", PutPolicy.PUT_ALWAYS);
345 
346         final CompletionStage<Optional<String>> get1 = cache().get("key");
347 
348         assertThat(get1, successfulWith(is(Optional.of("existing"))));
349 
350         cache().remove("key");
351 
352         final CompletionStage<Optional<String>> get2 = cache().get("key");
353 
354         assertThat(get2, successfulWith(is(Optional.empty())));
355 
356         final Map<MetricLabel, ? extends LongMetric> cacheMetrics =
357                 requestMetrics().allExternalCacheLongMetrics().get(CACHE_NAME);
358         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_GET_CALL, is(2L), greaterThan(0L)));
359         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_REMOVE_CALL, is(1L), greaterThan(0L)));
360         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_HITS, is(1L), is(1L)));
361         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_MISSES, is(1L), is(1L)));
362         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_REMOTE_GET, is(1L), is(1L)));
363         assertThat(cacheMetrics, hasSize(is(5)));
364     }
365 
366     @Test
367     public void existing_remove_getSupplier() {
368         directCache().put("key", "existing", PutPolicy.PUT_ALWAYS);
369 
370         final CompletionStage<Optional<String>> get1 = cache().get("key");
371 
372         assertThat(get1, successfulWith(is(Optional.of("existing"))));
373 
374         cache().remove("key");
375 
376         final CompletionStage<String> get2 = cache().get("key", () -> "new");
377 
378         assertThat(get2, successfulWith(is("new")));
379 
380         final Map<MetricLabel, ? extends LongMetric> cacheMetrics =
381                 requestMetrics().allExternalCacheLongMetrics().get(CACHE_NAME);
382         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_GET_CALL, is(2L), greaterThan(0L)));
383         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_REMOVE_CALL, is(1L), greaterThan(0L)));
384         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_SUPPLIER_CALL, is(1L), greaterThan(0L)));
385         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_HITS, is(1L), is(1L)));
386         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_MISSES, is(1L), is(1L)));
387         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_REMOTE_GET, is(1L), is(1L)));
388         assertThat(cacheMetrics, hasSize(is(6)));
389     }
390 
391     @Test
392     public void existing_remove_getBulk() {
393         directCache().put("key", "existing", PutPolicy.PUT_ALWAYS);
394 
395         final CompletionStage<Optional<String>> get1 = cache().get("key");
396 
397         assertThat(get1, successfulWith(is(Optional.of("existing"))));
398 
399         cache().remove("key");
400 
401         final CompletionStage<Map<String, String>> get2 = cache().getBulk(
402                 strings -> strings.stream().collect(Collectors.toMap(k -> k, k -> k + "-new")),
403                 "key", "extra");
404 
405         assertThat(get2, successful());
406         assertThat(unsafeJoin(get2).keySet(), containsInAnyOrder("key", "extra"));
407         assertThat(unsafeJoin(get2).values(), containsInAnyOrder("key-new", "extra-new"));
408 
409         final Map<MetricLabel, ? extends LongMetric> cacheMetrics =
410                 requestMetrics().allExternalCacheLongMetrics().get(CACHE_NAME);
411         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_GET_CALL, is(2L), greaterThan(0L)));
412         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_FACTORY_CALL, is(1L), greaterThan(0L)));
413         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_REMOVE_CALL, is(1L), greaterThan(0L)));
414         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_FACTORY_KEYS, is(1L), is(2L)));
415         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_HITS, is(1L), is(1L)));
416         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_MISSES, is(1L), is(2L)));
417         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_REMOTE_GET, is(2L), is(2L)));
418         assertThat(cacheMetrics, hasSize(is(7)));
419     }
420 
421     @Test
422     public void existing_removeAll_get() {
423         directCache().put("key", "existing", PutPolicy.PUT_ALWAYS);
424 
425         final CompletionStage<Optional<String>> get1 = cache().get("key");
426 
427         assertThat(get1, successfulWith(is(Optional.of("existing"))));
428 
429         cache().removeAll();
430 
431         final CompletionStage<Optional<String>> get2 = cache().get("key");
432 
433         assertThat(get2, successfulWith(is(Optional.empty())));
434 
435         final Map<MetricLabel, ? extends LongMetric> cacheMetrics =
436                 requestMetrics().allExternalCacheLongMetrics().get(CACHE_NAME);
437         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_GET_CALL, is(2L), greaterThan(0L)));
438         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_REMOVE_ALL_CALL, is(1L), greaterThan(0L)));
439         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_HITS, is(1L), is(1L)));
440         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_MISSES, is(1L), is(1L)));
441         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_REMOTE_GET, is(1L), is(1L)));
442         assertThat(cacheMetrics, hasSize(is(5)));
443     }
444 
445     @Test
446     public void existing_removeAll_getSupplier() {
447         directCache().put("key", "existing", PutPolicy.PUT_ALWAYS);
448 
449         final CompletionStage<Optional<String>> get1 = cache().get("key");
450 
451         assertThat(get1, successfulWith(is(Optional.of("existing"))));
452 
453         cache().removeAll();
454 
455         final CompletionStage<String> get2 = cache().get("key", () -> "new");
456 
457         assertThat(get2, successfulWith(is("new")));
458 
459         final Map<MetricLabel, ? extends LongMetric> cacheMetrics =
460                 requestMetrics().allExternalCacheLongMetrics().get(CACHE_NAME);
461         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_GET_CALL, is(2L), greaterThan(0L)));
462         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_REMOVE_ALL_CALL, is(1L), greaterThan(0L)));
463         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_SUPPLIER_CALL, is(1L), greaterThan(0L)));
464         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_HITS, is(1L), is(1L)));
465         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_MISSES, is(1L), is(1L)));
466         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_REMOTE_GET, is(1L), is(1L)));
467         assertThat(cacheMetrics, hasSize(is(6)));
468     }
469 
470     @Test
471     public void existing_removeAll_getBulk() {
472         directCache().put("key", "existing", PutPolicy.PUT_ALWAYS);
473 
474         final CompletionStage<Optional<String>> get1 = cache().get("key");
475 
476         assertThat(get1, successfulWith(is(Optional.of("existing"))));
477 
478         cache().removeAll();
479 
480         final CompletionStage<Map<String, String>> get2 = cache().getBulk(
481                 strings -> strings.stream().collect(Collectors.toMap(k -> k, k -> k + "-new")),
482                 "key");
483 
484         assertThat(get2, successful());
485         assertThat(unsafeJoin(get2).keySet(), containsInAnyOrder("key"));
486         assertThat(unsafeJoin(get2).values(), containsInAnyOrder("key-new"));
487 
488         final Map<MetricLabel, ? extends LongMetric> cacheMetrics =
489                 requestMetrics().allExternalCacheLongMetrics().get(CACHE_NAME);
490         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_GET_CALL, is(2L), greaterThan(0L)));
491         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_FACTORY_CALL, is(1L), greaterThan(0L)));
492         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_REMOVE_ALL_CALL, is(1L), greaterThan(0L)));
493         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_FACTORY_KEYS, is(1L), is(1L)));
494         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_HITS, is(1L), is(1L)));
495         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_MISSES, is(1L), is(1L)));
496         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_REMOTE_GET, is(1L), is(1L)));
497         assertThat(cacheMetrics, hasSize(is(7)));
498     }
499 
500     @Test
501     public void check_get_null_detection() {
502         assertThat(cache().get("kenny", () -> null), not(successful()));
503         assertThat(cache().getBulk(
504                 strings -> strings.stream().collect(Collectors.toMap(k -> k, k -> null)),
505                 "extra"),
506                 not(successful()));
507     }
508 
509     @SuppressWarnings("ConstantConditions")
510     @Test(expected = NullPointerException.class)
511     public void check_put_null_detection() {
512         cache().put("key", null, PutPolicy.ADD_ONLY);
513     }
514 }
515