View Javadoc

1   package com.atlassian.vcache.internal.test;
2   
3   import com.atlassian.vcache.ChangeRate;
4   import com.atlassian.vcache.DirectExternalCache;
5   import com.atlassian.vcache.ExternalCacheSettings;
6   import com.atlassian.vcache.ExternalCacheSettingsBuilder;
7   import com.atlassian.vcache.IdentifiedValue;
8   import com.atlassian.vcache.PutPolicy;
9   import com.atlassian.vcache.internal.LongMetric;
10  import com.atlassian.vcache.internal.MetricLabel;
11  import com.atlassian.vcache.internal.RequestMetrics;
12  import org.junit.Before;
13  import org.junit.Test;
14  
15  import java.time.Duration;
16  import java.util.EnumMap;
17  import java.util.Map;
18  import java.util.Optional;
19  import java.util.concurrent.CompletionStage;
20  import java.util.concurrent.ExecutionException;
21  import java.util.stream.Collectors;
22  
23  import static com.atlassian.vcache.VCacheUtils.unsafeJoin;
24  import static com.atlassian.vcache.internal.test.CacheMetricsMatcher.hasMetric;
25  import static com.atlassian.vcache.internal.test.CacheMetricsMatcher.hasSize;
26  import static com.atlassian.vcache.internal.test.CompletionStageSuccessful.successful;
27  import static com.atlassian.vcache.internal.test.CompletionStageSuccessful.successfulWith;
28  import static org.hamcrest.MatcherAssert.assertThat;
29  import static org.hamcrest.Matchers.containsInAnyOrder;
30  import static org.hamcrest.Matchers.equalTo;
31  import static org.hamcrest.Matchers.greaterThan;
32  import static org.hamcrest.Matchers.is;
33  import static org.hamcrest.Matchers.isOneOf;
34  import static org.hamcrest.Matchers.not;
35  import static org.hamcrest.Matchers.notNullValue;
36  import static org.hamcrest.Matchers.nullValue;
37  
38  /**
39   * Base test class for the {@link DirectExternalCache}.
40   */
41  @SuppressWarnings("OptionalGetWithoutIsPresent")
42  public abstract class AbstractDirectExternalCacheIT {
43      private static final String CACHE_NAME = "olympics";
44      private DirectExternalCache<String> cache;
45  
46      protected abstract DirectExternalCache<String> createCache(String name, ExternalCacheSettings settings);
47      protected abstract RequestMetrics requestMetrics();
48  
49      @Before
50      public void ensureCache() {
51          final ExternalCacheSettings settings = new ExternalCacheSettingsBuilder()
52                  .entryGrowthRateHint(ChangeRate.LOW_CHANGE)
53                  .entryCountHint(5)
54                  .defaultTtl(Duration.ofMinutes(5))
55                  .dataChangeRateHint(ChangeRate.HIGH_CHANGE)
56                  .build();
57          cache = createCache(CACHE_NAME, settings);
58          final CompletionStage<Void> rm = cache.removeAll();
59          assertThat(rm, successful());
60      }
61  
62      @Test
63      public void simple_get_set() throws ExecutionException, InterruptedException {
64          final CompletionStage<Optional<String>> eldestGet = cache.get("claira");
65  
66          assertThat(eldestGet, successfulWith(is(Optional.empty())));
67  
68          final CompletionStage<Boolean> eldestAdd = cache.put("claira", "dancing", PutPolicy.PUT_ALWAYS);
69  
70          assertThat(eldestAdd, successfulWith(is(true)));
71  
72          final CompletionStage<Optional<String>> eldestGet2 = cache.get("claira");
73  
74          assertThat(eldestGet2, successfulWith(is(Optional.of("dancing"))));
75  
76          final CompletionStage<Boolean> eldestAdd2 = cache.put("claira", "singing", PutPolicy.PUT_ALWAYS);
77  
78          assertThat(eldestAdd2, successfulWith(is(true)));
79  
80          final EnumMap<MetricLabel, ? extends LongMetric> cacheMetrics =
81                  requestMetrics().allExternalCacheLongMetrics().get(CACHE_NAME);
82          assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_GET_CALL, is(2L), greaterThan(0L)));
83          assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_PUT_CALL, is(2L), greaterThan(0L)));
84          assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_REMOVE_ALL_CALL, is(1L), greaterThan(0L)));
85          assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_HITS, is(1L), is(1L)));
86          assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_MISSES, is(1L), is(1L)));
87          assertThat(cacheMetrics, hasSize(is(5)));
88      }
89  
90      @Test
91      public void simple_get_add() throws ExecutionException, InterruptedException {
92          final CompletionStage<Optional<String>> eldestGet = cache.get("claira");
93  
94          assertThat(eldestGet, successfulWith(is(Optional.empty())));
95  
96          final CompletionStage<Boolean> eldestAdd = cache.put("claira", "dancing", PutPolicy.ADD_ONLY);
97  
98          assertThat(eldestAdd, successfulWith(is(true)));
99  
100         final CompletionStage<Optional<String>> eldestGet2 = cache.get("claira");
101 
102         assertThat(eldestGet2, successfulWith(is(Optional.of("dancing"))));
103 
104         final CompletionStage<Boolean> eldestAdd2 = cache.put("claira", "singing", PutPolicy.ADD_ONLY);
105 
106         assertThat(eldestAdd2, successfulWith(is(false)));
107 
108         final EnumMap<MetricLabel, ? extends LongMetric> cacheMetrics =
109                 requestMetrics().allExternalCacheLongMetrics().get(CACHE_NAME);
110         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_GET_CALL, is(2L), greaterThan(0L)));
111         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_PUT_CALL, is(2L), greaterThan(0L)));
112         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_REMOVE_ALL_CALL, is(1L), greaterThan(0L)));
113         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_HITS, is(1L), is(1L)));
114         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_MISSES, is(1L), is(1L)));
115         assertThat(cacheMetrics, hasSize(is(5)));
116     }
117 
118     @Test
119     public void simple_get_with_supplier() throws ExecutionException, InterruptedException {
120         final CompletionStage<Optional<String>> eldestGet1 = cache.get("josephine");
121 
122         assertThat(eldestGet1, successfulWith(is(Optional.empty())));
123 
124         final CompletionStage<String> eldestGet2 = cache.get("josephine", () -> "football");
125 
126         assertThat(eldestGet2, successfulWith(is("football")));
127 
128         final CompletionStage<Optional<String>> eldestGet3 = cache.get("josephine");
129 
130         assertThat(eldestGet3, successfulWith(is(Optional.of("football"))));
131 
132         final EnumMap<MetricLabel, ? extends LongMetric> cacheMetrics =
133                 requestMetrics().allExternalCacheLongMetrics().get(CACHE_NAME);
134         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_GET_CALL, is(3L), greaterThan(0L)));
135         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_REMOVE_ALL_CALL, is(1L), greaterThan(0L)));
136         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_SUPPLIER_CALL, is(1L), greaterThan(0L)));
137         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_HITS, is(1L), is(1L)));
138         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_MISSES, is(2L), is(2L)));
139         assertThat(cacheMetrics, hasSize(is(5)));
140     }
141 
142     @Test
143     public void put_with_huge_key() throws ExecutionException, InterruptedException {
144         final String key = "sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss"
145                 + "ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss"
146                 + "ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss"
147                 + "ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss"
148                 + "ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss"
149                 + "ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss"
150                 + "ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss"
151                 + "ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss"
152                 + "ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss"
153                 + "ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss"
154                 + "ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss"
155                 + "ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss"
156                 + "ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss"
157                 + "ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss"
158                 + "ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss"
159                 + "ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss"
160                 + "ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss"
161                 + "ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss";
162 
163         final CompletionStage<Boolean> hugeAdd = cache.put(key, "dancing", PutPolicy.ADD_ONLY);
164 
165         assertThat(hugeAdd, successfulWith(is(true)));
166 
167         final CompletionStage<Optional<String>> hugeGet = cache.get(key);
168 
169         assertThat(hugeGet, successfulWith(is(Optional.of("dancing"))));
170 
171         final EnumMap<MetricLabel, ? extends LongMetric> cacheMetrics =
172                 requestMetrics().allExternalCacheLongMetrics().get(CACHE_NAME);
173         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_GET_CALL, is(1L), greaterThan(0L)));
174         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_PUT_CALL, is(1L), greaterThan(0L)));
175         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_REMOVE_ALL_CALL, is(1L), greaterThan(0L)));
176         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_HITS, is(1L), is(1L)));
177         assertThat(cacheMetrics, hasSize(is(4)));
178     }
179 
180     @Test
181     public void simple_getBulkIdentified() throws ExecutionException, InterruptedException {
182         final CompletionStage<Boolean> put1 = cache.put("josie", "football", PutPolicy.PUT_ALWAYS);
183 
184         assertThat(put1, successfulWith(is(true)));
185 
186         final CompletionStage<Optional<IdentifiedValue<String>>> get1 = cache.getIdentified("josie");
187 
188         assertThat(get1, successful());
189         final IdentifiedValue<String> iv1 = unsafeJoin(get1).get();
190         assertThat(iv1.identifier(), notNullValue());
191         assertThat(iv1.value(), is("football"));
192 
193         final CompletionStage<Map<String, Optional<IdentifiedValue<String>>>> get2 =
194                 cache.getBulkIdentified("claira", "josie", "jasmin");
195 
196         assertThat(get2, successful());
197         final Map<String, Optional<IdentifiedValue<String>>> ivMap1 = unsafeJoin(get2);
198         assertThat(ivMap1.keySet(), containsInAnyOrder("jasmin", "claira", "josie"));
199         assertThat(ivMap1.get("claira"), is(Optional.empty()));
200         assertThat(ivMap1.get("jasmin"), is(Optional.empty()));
201         final IdentifiedValue<String> iv2 = ivMap1.get("josie").get();
202         assertThat(iv2.identifier(), notNullValue());
203         assertThat(iv2.identifier(), equalTo(iv1.identifier()));
204         assertThat(iv2.value(), is("football"));
205 
206         final EnumMap<MetricLabel, ? extends LongMetric> cacheMetrics =
207                 requestMetrics().allExternalCacheLongMetrics().get(CACHE_NAME);
208         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_IDENTIFIED_GET_CALL, is(2L), greaterThan(0L)));
209         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_PUT_CALL, is(1L), greaterThan(0L)));
210         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_REMOVE_ALL_CALL, is(1L), greaterThan(0L)));
211         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_HITS, is(2L), is(2L)));
212         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_MISSES, is(1L), is(2L)));
213         assertThat(cacheMetrics, hasSize(is(5)));
214     }
215 
216     @Test
217     public void simple_getIdentified_removeIf() throws ExecutionException, InterruptedException {
218         final CompletionStage<Optional<IdentifiedValue<String>>> get1 = cache.getIdentified("josie");
219 
220         assertThat(get1, successfulWith(is(Optional.empty())));
221 
222         final CompletionStage<Boolean> put1 = cache.put("josie", "football", PutPolicy.PUT_ALWAYS);
223 
224         assertThat(put1, successfulWith(is(true)));
225 
226         final CompletionStage<Optional<IdentifiedValue<String>>> get2 = cache.getIdentified("josie");
227 
228         assertThat(get2, successful());
229         final IdentifiedValue<String> iv2 = unsafeJoin(get2).get();
230         assertThat(iv2.identifier(), notNullValue());
231         assertThat(iv2.value(), is("football"));
232 
233         final CompletionStage<Boolean> rm1 = cache.removeIf("josie", iv2.identifier());
234 
235         assertThat(rm1, successfulWith(is(true)));
236 
237         final CompletionStage<Boolean> put2 = cache.put("josie", "football", PutPolicy.PUT_ALWAYS);
238 
239         assertThat(put2, successfulWith(is(true)));
240 
241         final CompletionStage<Optional<IdentifiedValue<String>>> get3 = cache.getIdentified("josie");
242 
243         assertThat(get3, successful());
244         final IdentifiedValue<String> iv3 = unsafeJoin(get3).get();
245         assertThat(iv3.identifier(), notNullValue());
246         assertThat(iv3.value(), is("football"));
247 
248         final CompletionStage<Boolean> rm3 = cache.removeIf("josie", iv3.identifier());
249 
250         assertThat(rm3, successfulWith(is(true)));
251 
252         final EnumMap<MetricLabel, ? extends LongMetric> cacheMetrics =
253                 requestMetrics().allExternalCacheLongMetrics().get(CACHE_NAME);
254         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_IDENTIFIED_GET_CALL, is(3L), greaterThan(0L)));
255         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_IDENTIFIED_REMOVE_CALL, is(2L), greaterThan(0L)));
256         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_PUT_CALL, is(2L), greaterThan(0L)));
257         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_REMOVE_ALL_CALL, is(1L), greaterThan(0L)));
258         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_HITS, is(2L), is(2L)));
259         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_MISSES, is(1L), is(1L)));
260         assertThat(cacheMetrics, hasSize(is(6)));
261     }
262 
263     @Test
264     public void simple_getIdentifiedSupplier_removeIf() throws ExecutionException, InterruptedException {
265         final CompletionStage<IdentifiedValue<String>> get1 = cache.getIdentified("josie", () -> "football");
266 
267         assertThat(get1, successful());
268         final IdentifiedValue<String> iv1 = unsafeJoin(get1);
269         assertThat(iv1.identifier(), notNullValue());
270         assertThat(iv1.value(), is("football"));
271 
272         final CompletionStage<Boolean> rm1 = cache.removeIf("josie", iv1.identifier());
273 
274         assertThat(rm1, successfulWith(is(true)));
275 
276         final CompletionStage<Boolean> put1 = cache.put("josie", "football", PutPolicy.PUT_ALWAYS);
277 
278         assertThat(put1, successfulWith(is(true)));
279 
280         final CompletionStage<IdentifiedValue<String>> get2 = cache.getIdentified("josie", () -> "swimming");
281 
282         assertThat(get2, successful());
283         final IdentifiedValue<String> iv2 = unsafeJoin(get2);
284         assertThat(iv2.identifier(), notNullValue());
285         assertThat(iv2.value(), is("football"));
286 
287         final CompletionStage<Boolean> rm2 = cache.removeIf("josie", iv2.identifier());
288 
289         assertThat(rm2, successfulWith(is(true)));
290 
291         final EnumMap<MetricLabel, ? extends LongMetric> cacheMetrics =
292                 requestMetrics().allExternalCacheLongMetrics().get(CACHE_NAME);
293         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_IDENTIFIED_GET_CALL, is(2L), greaterThan(0L)));
294         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_IDENTIFIED_REMOVE_CALL, is(2L), greaterThan(0L)));
295         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_PUT_CALL, is(1L), greaterThan(0L)));
296         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_REMOVE_ALL_CALL, is(1L), greaterThan(0L)));
297         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_SUPPLIER_CALL, is(1L), greaterThan(0L)));
298         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_HITS, is(1L), is(1L)));
299         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_MISSES, is(1L), is(1L)));
300         assertThat(cacheMetrics, hasSize(is(7)));
301     }
302 
303     /**
304      * Tests where the cas id is genuinely unique, and not just a copy of the original value.
305      */
306     @Test
307     public void exact_getIdentified_removeIf() throws ExecutionException, InterruptedException {
308         final CompletionStage<Boolean> putFirst = cache.put("josie", "football", PutPolicy.PUT_ALWAYS);
309 
310         assertThat(putFirst, successfulWith(is(true)));
311 
312         final CompletionStage<Optional<IdentifiedValue<String>>> getFirst = cache.getIdentified("josie");
313 
314         assertThat(getFirst, successful());
315         final IdentifiedValue<String> ivFirst = unsafeJoin(getFirst).get();
316         assertThat(ivFirst.identifier(), notNullValue());
317         assertThat(ivFirst.value(), is("football"));
318 
319         final CompletionStage<Boolean> putSecond = cache.put("josie", "football", PutPolicy.PUT_ALWAYS);
320 
321         assertThat(putSecond, successfulWith(is(true)));
322 
323         final CompletionStage<Boolean> rm1 = cache.removeIf("josie", ivFirst.identifier());
324 
325         assertThat(rm1, successfulWith(is(false)));
326 
327         final EnumMap<MetricLabel, ? extends LongMetric> cacheMetrics =
328                 requestMetrics().allExternalCacheLongMetrics().get(CACHE_NAME);
329         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_IDENTIFIED_GET_CALL, is(1L), greaterThan(0L)));
330         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_IDENTIFIED_REMOVE_CALL, is(1L), greaterThan(0L)));
331         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_PUT_CALL, is(2L), greaterThan(0L)));
332         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_REMOVE_ALL_CALL, is(1L), greaterThan(0L)));
333         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_HITS, is(1L), is(1L)));
334         assertThat(cacheMetrics, hasSize(is(5)));
335     }
336 
337     @Test
338     public void simple_getIdentified_replaceIf() throws ExecutionException, InterruptedException {
339         final CompletionStage<Boolean> put1 = cache.put("josie", "football", PutPolicy.PUT_ALWAYS);
340 
341         assertThat(put1, successfulWith(is(true)));
342 
343         final CompletionStage<Optional<IdentifiedValue<String>>> get1 = cache.getIdentified("josie");
344 
345         assertThat(get1, successful());
346         final IdentifiedValue<String> iv1 = unsafeJoin(get1).get();
347         assertThat(iv1.identifier(), notNullValue());
348         assertThat(iv1.value(), is("football"));
349 
350         final CompletionStage<Boolean> rm1 = cache.replaceIf("josie", iv1.identifier(), "soccer");
351 
352         assertThat(rm1, successfulWith(is(true)));
353 
354         final CompletionStage<Boolean> put2 = cache.put("josie", "football", PutPolicy.PUT_ALWAYS);
355 
356         assertThat(put2, successfulWith(is(true)));
357 
358         final CompletionStage<Optional<IdentifiedValue<String>>> get3 = cache.getIdentified("josie");
359 
360         assertThat(get3, successful());
361         final IdentifiedValue<String> iv3 = unsafeJoin(get3).get();
362         assertThat(iv3.identifier(), notNullValue());
363         assertThat(iv3.value(), is("football"));
364 
365         final CompletionStage<Boolean> rm3 = cache.removeIf("josie", iv3.identifier());
366 
367         assertThat(rm3, successfulWith(is(true)));
368 
369         final EnumMap<MetricLabel, ? extends LongMetric> cacheMetrics =
370                 requestMetrics().allExternalCacheLongMetrics().get(CACHE_NAME);
371         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_IDENTIFIED_GET_CALL, is(2L), greaterThan(0L)));
372         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_IDENTIFIED_REPLACE_CALL, is(1L), greaterThan(0L)));
373         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_IDENTIFIED_REMOVE_CALL, is(1L), greaterThan(0L)));
374         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_PUT_CALL, is(2L), greaterThan(0L)));
375         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_REMOVE_ALL_CALL, is(1L), greaterThan(0L)));
376         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_HITS, is(2L), is(2L)));
377         assertThat(cacheMetrics, hasSize(is(6)));
378     }
379 
380     /**
381      * Tests where the cas id is genuinely unique, and not just a copy of the original value.
382      */
383     @Test
384     public void exact_getIdentified_replaceIf() throws ExecutionException, InterruptedException {
385         final CompletionStage<Boolean> putFirst = cache.put("josie", "football", PutPolicy.PUT_ALWAYS);
386 
387         assertThat(putFirst, successfulWith(is(true)));
388 
389         final CompletionStage<Optional<IdentifiedValue<String>>> getFirst = cache.getIdentified("josie");
390 
391         assertThat(getFirst, successful());
392         final IdentifiedValue<String> ivFirst = unsafeJoin(getFirst).get();
393         assertThat(ivFirst.identifier(), notNullValue());
394         assertThat(ivFirst.value(), is("football"));
395 
396         final CompletionStage<Boolean> putSecond = cache.put("josie", "football", PutPolicy.PUT_ALWAYS);
397 
398         assertThat(putSecond, successfulWith(is(true)));
399 
400         final CompletionStage<Boolean> rm1 = cache.replaceIf("josie", ivFirst.identifier(), "afl");
401 
402         assertThat(rm1, successfulWith(is(false)));
403 
404         final EnumMap<MetricLabel, ? extends LongMetric> cacheMetrics =
405                 requestMetrics().allExternalCacheLongMetrics().get(CACHE_NAME);
406         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_IDENTIFIED_GET_CALL, is(1L), greaterThan(0L)));
407         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_IDENTIFIED_REPLACE_CALL, is(1L), greaterThan(0L)));
408         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_PUT_CALL, is(2L), greaterThan(0L)));
409         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_REMOVE_ALL_CALL, is(1L), greaterThan(0L)));
410         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_HITS, is(1L), is(1L)));
411         assertThat(cacheMetrics, hasSize(is(5)));
412     }
413 
414     @Test
415     public void simple_put_remove() throws ExecutionException, InterruptedException {
416         final CompletionStage<Void> rm1 = cache.remove("claira");
417 
418         assertThat(rm1, successful());
419         assertThat(unsafeJoin(rm1), nullValue());
420 
421         final CompletionStage<Boolean> put1 = cache.put("claira", "dancing", PutPolicy.PUT_ALWAYS);
422 
423         assertThat(put1, successfulWith(is(true)));
424 
425         final CompletionStage<Void> rm2 = cache.remove("claira");
426 
427         assertThat(rm2, successful());
428 
429         final CompletionStage<Boolean> put2 = cache.put("claira", "dancing", PutPolicy.PUT_ALWAYS);
430 
431         assertThat(put2, successfulWith(is(true)));
432 
433         final CompletionStage<Optional<String>> get1 = cache.get("claira");
434 
435         assertThat(get1, successfulWith(is(Optional.of("dancing"))));
436 
437         final CompletionStage<Void> rm3 = cache.remove("josie", "claira", "jasmin");
438 
439         assertThat(rm3, successful());
440 
441         final CompletionStage<Optional<String>> get2 = cache.get("claira");
442 
443         assertThat(get2, successfulWith(is(Optional.empty())));
444 
445         final EnumMap<MetricLabel, ? extends LongMetric> cacheMetrics =
446                 requestMetrics().allExternalCacheLongMetrics().get(CACHE_NAME);
447         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_GET_CALL, is(2L), greaterThan(0L)));
448         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_PUT_CALL, is(2L), greaterThan(0L)));
449         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_REMOVE_CALL, is(3L), greaterThan(0L)));
450         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_REMOVE_ALL_CALL, is(1L), greaterThan(0L)));
451         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_HITS, is(1L), is(1L)));
452         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_MISSES, is(1L), is(1L)));
453         assertThat(cacheMetrics, hasSize(is(6)));
454     }
455 
456     @Test
457     public void simple_removeAll() throws ExecutionException, InterruptedException {
458         final CompletionStage<Boolean> put1 = cache.put("claira", "", PutPolicy.PUT_ALWAYS);
459 
460         assertThat(put1, successfulWith(is(true)));
461 
462         final CompletionStage<Optional<String>> get1 = cache.get("claira");
463 
464         assertThat(get1, successfulWith(is(Optional.of(""))));
465 
466         final CompletionStage<Void> rm1 = cache.removeAll();
467 
468         assertThat(rm1, successful());
469 
470         final CompletionStage<Optional<String>> get2 = cache.get("claira");
471 
472         assertThat(get2, successfulWith(is(Optional.empty())));
473 
474         final EnumMap<MetricLabel, ? extends LongMetric> cacheMetrics =
475                 requestMetrics().allExternalCacheLongMetrics().get(CACHE_NAME);
476         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_GET_CALL, is(2L), greaterThan(0L)));
477         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_PUT_CALL, is(1L), greaterThan(0L)));
478         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_REMOVE_ALL_CALL, is(2L), greaterThan(0L)));
479         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_HITS, is(1L), is(1L)));
480         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_MISSES, is(1L), is(1L)));
481         assertThat(cacheMetrics, hasSize(is(5)));
482     }
483 
484     @SuppressWarnings("unchecked")
485     @Test
486     public void simple_getBulk() throws ExecutionException, InterruptedException {
487         final CompletionStage<Map<String, Optional<String>>> get1 = cache.getBulk("claira", "jasmin", "josie", "josie");
488 
489         assertThat(get1, successful());
490         assertThat(unsafeJoin(get1).keySet(), containsInAnyOrder("claira", "jasmin", "josie"));
491         assertThat(unsafeJoin(get1).values(), containsInAnyOrder(Optional.empty(), Optional.empty(), Optional.empty()));
492 
493         final CompletionStage<Boolean> put1 = cache.put("claira", "youtube", PutPolicy.PUT_ALWAYS);
494 
495         assertThat(put1, successfulWith(is(true)));
496 
497         final CompletionStage<Map<String, Optional<String>>> get2 = cache.getBulk("jasmin", "claira", "josie", "claira");
498 
499         assertThat(get2, successful());
500         assertThat(unsafeJoin(get2).keySet(), containsInAnyOrder("claira", "jasmin", "josie"));
501         assertThat(unsafeJoin(get2).values(), containsInAnyOrder(Optional.of("youtube"), Optional.empty(), Optional.empty()));
502 
503         final EnumMap<MetricLabel, ? extends LongMetric> cacheMetrics =
504                 requestMetrics().allExternalCacheLongMetrics().get(CACHE_NAME);
505         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_GET_CALL, is(2L), greaterThan(0L)));
506         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_PUT_CALL, is(1L), greaterThan(0L)));
507         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_REMOVE_ALL_CALL, is(1L), greaterThan(0L)));
508         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_HITS, is(1L), is(1L)));
509         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_MISSES, is(2L), is(5L)));
510         assertThat(cacheMetrics, hasSize(is(5)));
511     }
512 
513     @Test
514     public void simple_getBulkFactory() throws ExecutionException, InterruptedException {
515         final CompletionStage<Map<String, String>> get1 =
516                 cache.getBulk(
517                         strings -> strings.stream().collect(Collectors.toMap(k -> k, k -> k + "-1")),
518                         "claira", "josie", "josie");
519 
520         assertThat(get1, successful());
521         assertThat(unsafeJoin(get1).keySet(), containsInAnyOrder("claira", "josie"));
522         assertThat(unsafeJoin(get1).values(), containsInAnyOrder("claira-1", "josie-1"));
523 
524         final CompletionStage<Map<String, String>> get2 =
525                 cache.getBulk(
526                         strings -> strings.stream().collect(Collectors.toMap(k -> k, k -> k + "-2")),
527                         "claira", "josie", "jasmin");
528 
529         assertThat(get2, successful());
530         assertThat(unsafeJoin(get2).keySet(), containsInAnyOrder("claira", "josie", "jasmin"));
531         assertThat(unsafeJoin(get2).values(), containsInAnyOrder("claira-1", "josie-1", "jasmin-2"));
532 
533         final EnumMap<MetricLabel, ? extends LongMetric> cacheMetrics =
534                 requestMetrics().allExternalCacheLongMetrics().get(CACHE_NAME);
535         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_GET_CALL, is(2L), greaterThan(0L)));
536         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_FACTORY_CALL, is(2L), greaterThan(0L)));
537         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_REMOVE_ALL_CALL, is(1L), greaterThan(0L)));
538         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_FACTORY_KEYS, is(2L), is(3L)));
539         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_HITS, is(1L), is(2L)));
540         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_MISSES, is(2L), is(3L)));
541         assertThat(cacheMetrics, hasSize(is(6)));
542     }
543 
544     @SuppressWarnings("ConstantConditions")
545     @Test
546     public void check_null_detection() {
547         assertThat(cache.get("kenny", () -> null), not(successful()));
548         assertThat(cache.getIdentified("norway", () -> null), not(successful()));
549         assertThat(cache.put("key", null, PutPolicy.ADD_ONLY), not(successful()));
550         assertThat(cache.getBulk(
551                 strings -> strings.stream().collect(Collectors.toMap(k -> k, k -> null)),
552                 "extra"),
553                 not(successful()));
554 
555         final EnumMap<MetricLabel, ? extends LongMetric> cacheMetrics =
556                 requestMetrics().allExternalCacheLongMetrics().get(CACHE_NAME);
557         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_FACTORY_CALL, is(1L), greaterThan(0L)));
558         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_GET_CALL, is(2L), greaterThan(0L)));
559         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_IDENTIFIED_GET_CALL, is(1L), greaterThan(0L)));
560         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_PUT_CALL, is(1L), greaterThan(0L)));
561         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_REMOVE_ALL_CALL, is(1L), greaterThan(0L)));
562         // isOneOf() on next line as Legacy test will fail in a different way when CAS not supported
563         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_SUPPLIER_CALL, isOneOf(1L, 2L), greaterThan(0L)));
564         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_FACTORY_KEYS, is(1L), is(1L)));
565         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_FAILED_GET, is(2L), is(2L)));
566         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_FAILED_IDENTIFIED_GET, is(1L), is(1L)));
567         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_FAILED_PUT, is(1L), is(1L)));
568         assertThat(cacheMetrics, hasSize(is(10)));
569     }
570 
571     @SuppressWarnings("ConstantConditions")
572     @Test
573     public void check_null_detection_with_cas() {
574         final CompletionStage<IdentifiedValue<String>> geti = cache.getIdentified("temp", () -> "value");
575         assertThat(geti, successful());
576         assertThat(cache.replaceIf("temp", unsafeJoin(geti).identifier(), null), not(successful()));
577 
578         final EnumMap<MetricLabel, ? extends LongMetric> cacheMetrics =
579                 requestMetrics().allExternalCacheLongMetrics().get(CACHE_NAME);
580         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_IDENTIFIED_GET_CALL, is(1L), greaterThan(0L)));
581         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_IDENTIFIED_REPLACE_CALL, is(1L), greaterThan(0L)));
582         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_SUPPLIER_CALL, is(1L), greaterThan(0L)));
583         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_REMOVE_ALL_CALL, is(1L), greaterThan(0L)));
584         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_FAILED_IDENTIFIED_REPLACE, is(1L), is(1L)));
585         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_MISSES, is(1L), is(1L)));
586         assertThat(cacheMetrics, hasSize(is(6)));
587     }
588 
589     @SuppressWarnings("ConstantConditions")
590     @Test
591     public void check_null_key() {
592         final CompletionStage<Boolean> put1 = cache.put(null, "value", PutPolicy.PUT_ALWAYS);
593         assertThat(put1, not(successful()));
594 
595         final EnumMap<MetricLabel, ? extends LongMetric> cacheMetrics =
596                 requestMetrics().allExternalCacheLongMetrics().get(CACHE_NAME);
597         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_PUT_CALL, is(1L), greaterThan(0L)));
598         assertThat(cacheMetrics, hasMetric(MetricLabel.TIMED_REMOVE_ALL_CALL, is(1L), greaterThan(0L)));
599         assertThat(cacheMetrics, hasMetric(MetricLabel.NUMBER_OF_FAILED_PUT, is(1L), is(1L)));
600         assertThat(cacheMetrics, hasSize(is(3)));
601     }
602 
603     @Test
604     public void potential_deadlock() {
605         final CompletionStage<String> get = cache.get("lockType", () -> {
606             // People actually do this!!
607             final CompletionStage<Void> rm = cache.remove("lockType");
608             assertThat(rm, successful());
609 
610             return "lockwood";
611         });
612         assertThat(get, successfulWith(is("lockwood")));
613     }
614 }