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