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 org.junit.Before;
10  import org.junit.Test;
11  
12  import java.time.Duration;
13  import java.util.Map;
14  import java.util.Optional;
15  import java.util.concurrent.CompletionStage;
16  import java.util.concurrent.ExecutionException;
17  import java.util.stream.Collectors;
18  
19  import static com.atlassian.vcache.VCacheUtils.unsafeJoin;
20  import static com.atlassian.vcache.internal.test.CompletionStageSuccessful.successful;
21  import static com.atlassian.vcache.internal.test.CompletionStageSuccessful.successfulWith;
22  import static org.hamcrest.MatcherAssert.assertThat;
23  import static org.hamcrest.Matchers.containsInAnyOrder;
24  import static org.hamcrest.Matchers.equalTo;
25  import static org.hamcrest.Matchers.is;
26  import static org.hamcrest.Matchers.not;
27  import static org.hamcrest.Matchers.notNullValue;
28  import static org.hamcrest.Matchers.nullValue;
29  
30  /**
31   * Base test class for the {@link DirectExternalCache}.
32   */
33  @SuppressWarnings("OptionalGetWithoutIsPresent")
34  public abstract class AbstractDirectExternalCacheIT {
35      private DirectExternalCache<String> cache;
36  
37      protected abstract DirectExternalCache<String> createCache(String name, ExternalCacheSettings settings);
38  
39      @Before
40      public void ensureCache() {
41          final ExternalCacheSettings settings = new ExternalCacheSettingsBuilder()
42                  .entryGrowthRateHint(ChangeRate.LOW_CHANGE)
43                  .entryCountHint(5)
44                  .defaultTtl(Duration.ofMinutes(5))
45                  .dataChangeRateHint(ChangeRate.HIGH_CHANGE)
46                  .build();
47          cache = createCache("abc", settings);
48          final CompletionStage<Void> rm = cache.removeAll();
49          assertThat(rm, successful());
50      }
51  
52      @Test
53      public void simple_get_set() throws ExecutionException, InterruptedException {
54          final CompletionStage<Optional<String>> eldestGet = cache.get("claira");
55  
56          assertThat(eldestGet, successfulWith(is(Optional.empty())));
57  
58          final CompletionStage<Boolean> eldestAdd = cache.put("claira", "dancing", PutPolicy.PUT_ALWAYS);
59  
60          assertThat(eldestAdd, successfulWith(is(true)));
61  
62          final CompletionStage<Optional<String>> eldestGet2 = cache.get("claira");
63  
64          assertThat(eldestGet2, successfulWith(is(Optional.of("dancing"))));
65  
66          final CompletionStage<Boolean> eldestAdd2 = cache.put("claira", "singing", PutPolicy.PUT_ALWAYS);
67  
68          assertThat(eldestAdd2, successfulWith(is(true)));
69      }
70  
71      @Test
72      public void simple_get_add() throws ExecutionException, InterruptedException {
73          final CompletionStage<Optional<String>> eldestGet = cache.get("claira");
74  
75          assertThat(eldestGet, successfulWith(is(Optional.empty())));
76  
77          final CompletionStage<Boolean> eldestAdd = cache.put("claira", "dancing", PutPolicy.ADD_ONLY);
78  
79          assertThat(eldestAdd, successfulWith(is(true)));
80  
81          final CompletionStage<Optional<String>> eldestGet2 = cache.get("claira");
82  
83          assertThat(eldestGet2, successfulWith(is(Optional.of("dancing"))));
84  
85          final CompletionStage<Boolean> eldestAdd2 = cache.put("claira", "singing", PutPolicy.ADD_ONLY);
86  
87          assertThat(eldestAdd2, successfulWith(is(false)));
88      }
89  
90      @Test
91      public void simple_get_with_supplier() throws ExecutionException, InterruptedException {
92          final CompletionStage<Optional<String>> eldestGet1 = cache.get("josephine");
93  
94          assertThat(eldestGet1, successfulWith(is(Optional.empty())));
95  
96          final CompletionStage<String> eldestGet2 = cache.get("josephine", () -> "football");
97  
98          assertThat(eldestGet2, successfulWith(is("football")));
99  
100         final CompletionStage<Optional<String>> eldestGet3 = cache.get("josephine");
101 
102         assertThat(eldestGet3, successfulWith(is(Optional.of("football"))));
103     }
104 
105     @Test
106     public void put_with_huge_key() throws ExecutionException, InterruptedException {
107         final String key = "sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss"
108                 + "ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss"
109                 + "ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss"
110                 + "ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss"
111                 + "ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss"
112                 + "ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss"
113                 + "ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss"
114                 + "ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss"
115                 + "ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss"
116                 + "ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss"
117                 + "ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss"
118                 + "ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss"
119                 + "ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss"
120                 + "ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss"
121                 + "ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss"
122                 + "ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss"
123                 + "ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss"
124                 + "ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss";
125 
126         final CompletionStage<Boolean> hugeAdd = cache.put(key, "dancing", PutPolicy.ADD_ONLY);
127 
128         assertThat(hugeAdd, successfulWith(is(true)));
129 
130         final CompletionStage<Optional<String>> hugeGet = cache.get(key);
131 
132         assertThat(hugeGet, successfulWith(is(Optional.of("dancing"))));
133     }
134 
135     @Test
136     public void simple_getBulkIdentified() throws ExecutionException, InterruptedException {
137         final CompletionStage<Boolean> put1 = cache.put("josie", "football", PutPolicy.PUT_ALWAYS);
138 
139         assertThat(put1, successfulWith(is(true)));
140 
141         final CompletionStage<Optional<IdentifiedValue<String>>> get1 = cache.getIdentified("josie");
142 
143         assertThat(get1, successful());
144         final IdentifiedValue<String> iv1 = unsafeJoin(get1).get();
145         assertThat(iv1.identifier(), notNullValue());
146         assertThat(iv1.value(), is("football"));
147 
148         final CompletionStage<Map<String, Optional<IdentifiedValue<String>>>> get2 =
149                 cache.getBulkIdentified("claira", "josie", "jasmin");
150 
151         assertThat(get2, successful());
152         final Map<String, Optional<IdentifiedValue<String>>> ivMap1 = unsafeJoin(get2);
153         assertThat(ivMap1.keySet(), containsInAnyOrder("jasmin", "claira", "josie"));
154         assertThat(ivMap1.get("claira"), is(Optional.empty()));
155         assertThat(ivMap1.get("jasmin"), is(Optional.empty()));
156         final IdentifiedValue<String> iv2 = ivMap1.get("josie").get();
157         assertThat(iv2.identifier(), notNullValue());
158         assertThat(iv2.identifier(), equalTo(iv1.identifier()));
159         assertThat(iv2.value(), is("football"));
160     }
161 
162     @Test
163     public void simple_getIdentified_removeIf() throws ExecutionException, InterruptedException {
164         final CompletionStage<Optional<IdentifiedValue<String>>> get1 = cache.getIdentified("josie");
165 
166         assertThat(get1, successfulWith(is(Optional.empty())));
167 
168         final CompletionStage<Boolean> put1 = cache.put("josie", "football", PutPolicy.PUT_ALWAYS);
169 
170         assertThat(put1, successfulWith(is(true)));
171 
172         final CompletionStage<Optional<IdentifiedValue<String>>> get2 = cache.getIdentified("josie");
173 
174         assertThat(get2, successful());
175         final IdentifiedValue<String> iv2 = unsafeJoin(get2).get();
176         assertThat(iv2.identifier(), notNullValue());
177         assertThat(iv2.value(), is("football"));
178 
179         final CompletionStage<Boolean> rm1 = cache.removeIf("josie", iv2.identifier());
180 
181         assertThat(rm1, successfulWith(is(true)));
182 
183         final CompletionStage<Boolean> put2 = cache.put("josie", "football", PutPolicy.PUT_ALWAYS);
184 
185         assertThat(put2, successfulWith(is(true)));
186 
187         final CompletionStage<Optional<IdentifiedValue<String>>> get3 = cache.getIdentified("josie");
188 
189         assertThat(get3, successful());
190         final IdentifiedValue<String> iv3 = unsafeJoin(get3).get();
191         assertThat(iv3.identifier(), notNullValue());
192         assertThat(iv3.value(), is("football"));
193 
194         final CompletionStage<Boolean> rm3 = cache.removeIf("josie", iv3.identifier());
195 
196         assertThat(rm3, successfulWith(is(true)));
197     }
198 
199     @Test
200     public void simple_getIdentifiedSupplier_removeIf() throws ExecutionException, InterruptedException {
201         final CompletionStage<IdentifiedValue<String>> get1 = cache.getIdentified("josie", () -> "football");
202 
203         assertThat(get1, successful());
204         final IdentifiedValue<String> iv1 = unsafeJoin(get1);
205         assertThat(iv1.identifier(), notNullValue());
206         assertThat(iv1.value(), is("football"));
207 
208         final CompletionStage<Boolean> rm1 = cache.removeIf("josie", iv1.identifier());
209 
210         assertThat(rm1, successfulWith(is(true)));
211 
212         final CompletionStage<Boolean> put1 = cache.put("josie", "football", PutPolicy.PUT_ALWAYS);
213 
214         assertThat(put1, successfulWith(is(true)));
215 
216         final CompletionStage<IdentifiedValue<String>> get2 = cache.getIdentified("josie", () -> "swimming");
217 
218         assertThat(get2, successful());
219         final IdentifiedValue<String> iv2 = unsafeJoin(get2);
220         assertThat(iv2.identifier(), notNullValue());
221         assertThat(iv2.value(), is("football"));
222 
223         final CompletionStage<Boolean> rm2 = cache.removeIf("josie", iv2.identifier());
224 
225         assertThat(rm2, successfulWith(is(true)));
226     }
227 
228     /**
229      * Tests where the cas id is genuinely unique, and not just a copy of the original value.
230      */
231     @Test
232     public void exact_getIdentified_removeIf() throws ExecutionException, InterruptedException {
233         final CompletionStage<Boolean> putFirst = cache.put("josie", "football", PutPolicy.PUT_ALWAYS);
234 
235         assertThat(putFirst, successfulWith(is(true)));
236 
237         final CompletionStage<Optional<IdentifiedValue<String>>> getFirst = cache.getIdentified("josie");
238 
239         assertThat(getFirst, successful());
240         final IdentifiedValue<String> ivFirst = unsafeJoin(getFirst).get();
241         assertThat(ivFirst.identifier(), notNullValue());
242         assertThat(ivFirst.value(), is("football"));
243 
244         final CompletionStage<Boolean> putSecond = cache.put("josie", "football", PutPolicy.PUT_ALWAYS);
245 
246         assertThat(putSecond, successfulWith(is(true)));
247 
248         final CompletionStage<Boolean> rm1 = cache.removeIf("josie", ivFirst.identifier());
249 
250         assertThat(rm1, successfulWith(is(false)));
251     }
252 
253     @Test
254     public void simple_getIdentified_replaceIf() throws ExecutionException, InterruptedException {
255         final CompletionStage<Boolean> put1 = cache.put("josie", "football", PutPolicy.PUT_ALWAYS);
256 
257         assertThat(put1, successfulWith(is(true)));
258 
259         final CompletionStage<Optional<IdentifiedValue<String>>> get1 = cache.getIdentified("josie");
260 
261         assertThat(get1, successful());
262         final IdentifiedValue<String> iv1 = unsafeJoin(get1).get();
263         assertThat(iv1.identifier(), notNullValue());
264         assertThat(iv1.value(), is("football"));
265 
266         final CompletionStage<Boolean> rm1 = cache.replaceIf("josie", iv1.identifier(), "soccer");
267 
268         assertThat(rm1, successfulWith(is(true)));
269 
270         final CompletionStage<Boolean> put2 = cache.put("josie", "football", PutPolicy.PUT_ALWAYS);
271 
272         assertThat(put2, successfulWith(is(true)));
273 
274         final CompletionStage<Optional<IdentifiedValue<String>>> get3 = cache.getIdentified("josie");
275 
276         assertThat(get3, successful());
277         final IdentifiedValue<String> iv3 = unsafeJoin(get3).get();
278         assertThat(iv3.identifier(), notNullValue());
279         assertThat(iv3.value(), is("football"));
280 
281         final CompletionStage<Boolean> rm3 = cache.removeIf("josie", iv3.identifier());
282 
283         assertThat(rm3, successfulWith(is(true)));
284     }
285 
286     /**
287      * Tests where the cas id is genuinely unique, and not just a copy of the original value.
288      */
289     @Test
290     public void exact_getIdentified_replaceIf() throws ExecutionException, InterruptedException {
291         final CompletionStage<Boolean> putFirst = cache.put("josie", "football", PutPolicy.PUT_ALWAYS);
292 
293         assertThat(putFirst, successfulWith(is(true)));
294 
295         final CompletionStage<Optional<IdentifiedValue<String>>> getFirst = cache.getIdentified("josie");
296 
297         assertThat(getFirst, successful());
298         final IdentifiedValue<String> ivFirst = unsafeJoin(getFirst).get();
299         assertThat(ivFirst.identifier(), notNullValue());
300         assertThat(ivFirst.value(), is("football"));
301 
302         final CompletionStage<Boolean> putSecond = cache.put("josie", "football", PutPolicy.PUT_ALWAYS);
303 
304         assertThat(putSecond, successfulWith(is(true)));
305 
306         final CompletionStage<Boolean> rm1 = cache.replaceIf("josie", ivFirst.identifier(), "afl");
307 
308         assertThat(rm1, successfulWith(is(false)));
309     }
310 
311     @Test
312     public void simple_put_remove() throws ExecutionException, InterruptedException {
313         final CompletionStage<Void> rm1 = cache.remove("claira");
314 
315         assertThat(rm1, successful());
316         assertThat(unsafeJoin(rm1), nullValue());
317 
318         final CompletionStage<Boolean> put1 = cache.put("claira", "dancing", PutPolicy.PUT_ALWAYS);
319 
320         assertThat(put1, successfulWith(is(true)));
321 
322         final CompletionStage<Void> rm2 = cache.remove("claira");
323 
324         assertThat(rm2, successful());
325 
326         final CompletionStage<Boolean> put2 = cache.put("claira", "dancing", PutPolicy.PUT_ALWAYS);
327 
328         assertThat(put2, successfulWith(is(true)));
329 
330         final CompletionStage<Optional<String>> get1 = cache.get("claira");
331 
332         assertThat(get1, successfulWith(is(Optional.of("dancing"))));
333 
334         final CompletionStage<Void> rm3 = cache.remove("josie", "claira", "jasmin");
335 
336         assertThat(rm3, successful());
337 
338         final CompletionStage<Optional<String>> get2 = cache.get("claira");
339 
340         assertThat(get2, successfulWith(is(Optional.empty())));
341     }
342 
343     @Test
344     public void simple_removeAll() throws ExecutionException, InterruptedException {
345         final CompletionStage<Boolean> put1 = cache.put("claira", "", PutPolicy.PUT_ALWAYS);
346 
347         assertThat(put1, successfulWith(is(true)));
348 
349         final CompletionStage<Optional<String>> get1 = cache.get("claira");
350 
351         assertThat(get1, successfulWith(is(Optional.of(""))));
352 
353         final CompletionStage<Void> rm1 = cache.removeAll();
354 
355         assertThat(rm1, successful());
356 
357         final CompletionStage<Optional<String>> get2 = cache.get("claira");
358 
359         assertThat(get2, successfulWith(is(Optional.empty())));
360     }
361 
362     @SuppressWarnings("unchecked")
363     @Test
364     public void simple_getBulk() throws ExecutionException, InterruptedException {
365         final CompletionStage<Map<String, Optional<String>>> get1 = cache.getBulk("claira", "jasmin", "josie", "josie");
366 
367         assertThat(get1, successful());
368         assertThat(unsafeJoin(get1).keySet(), containsInAnyOrder("claira", "jasmin", "josie"));
369         assertThat(unsafeJoin(get1).values(), containsInAnyOrder(Optional.empty(), Optional.empty(), Optional.empty()));
370 
371         final CompletionStage<Boolean> put1 = cache.put("claira", "youtube", PutPolicy.PUT_ALWAYS);
372 
373         assertThat(put1, successfulWith(is(true)));
374 
375         final CompletionStage<Map<String, Optional<String>>> get2 = cache.getBulk("jasmin", "claira", "josie", "claira");
376 
377         assertThat(get2, successful());
378         assertThat(unsafeJoin(get2).keySet(), containsInAnyOrder("claira", "jasmin", "josie"));
379         assertThat(unsafeJoin(get2).values(), containsInAnyOrder(Optional.of("youtube"), Optional.empty(), Optional.empty()));
380     }
381 
382     @Test
383     public void simple_getBulkFactory() throws ExecutionException, InterruptedException {
384         final CompletionStage<Map<String, String>> get1 =
385                 cache.getBulk(
386                         strings -> strings.stream().collect(Collectors.toMap(k -> k, k -> k + "-1")),
387                         "claira", "josie", "josie");
388 
389         assertThat(get1, successful());
390         assertThat(unsafeJoin(get1).keySet(), containsInAnyOrder("claira", "josie"));
391         assertThat(unsafeJoin(get1).values(), containsInAnyOrder("claira-1", "josie-1"));
392 
393         final CompletionStage<Map<String, String>> get2 =
394                 cache.getBulk(
395                         strings -> strings.stream().collect(Collectors.toMap(k -> k, k -> k + "-2")),
396                         "claira", "josie", "jasmin");
397 
398         assertThat(get2, successful());
399         assertThat(unsafeJoin(get2).keySet(), containsInAnyOrder("claira", "josie", "jasmin"));
400         assertThat(unsafeJoin(get2).values(), containsInAnyOrder("claira-1", "josie-1", "jasmin-2"));
401     }
402 
403     @SuppressWarnings("ConstantConditions")
404     @Test
405     public void check_null_detection() {
406         assertThat(cache.get("kenny", () -> null), not(successful()));
407         assertThat(cache.getIdentified("norway", () -> null), not(successful()));
408         assertThat(cache.put("key", null, PutPolicy.ADD_ONLY), not(successful()));
409         assertThat(cache.getBulk(
410                 strings -> strings.stream().collect(Collectors.toMap(k -> k, k -> null)),
411                 "extra"),
412                 not(successful()));
413     }
414 
415     @SuppressWarnings("ConstantConditions")
416     @Test
417     public void check_null_detection_with_cas() {
418         final CompletionStage<IdentifiedValue<String>> geti = cache.getIdentified("temp", () -> "value");
419         assertThat(geti, successful());
420         assertThat(cache.replaceIf("temp", unsafeJoin(geti).identifier(), null), not(successful()));
421     }
422 
423     @SuppressWarnings("ConstantConditions")
424     @Test
425     public void check_null_key() {
426         final CompletionStage<Boolean> put1 = cache.put(null, "value", PutPolicy.PUT_ALWAYS);
427         assertThat(put1, not(successful()));
428     }
429 
430     @Test
431     public void potential_deadlock() {
432         final CompletionStage<String> get = cache.get("lockType", () -> {
433             // People actually do this!!
434             final CompletionStage<Void> rm = cache.remove("lockType");
435             assertThat(rm, successful());
436 
437             return "lockwood";
438         });
439         assertThat(get, successfulWith(is("lockwood")));
440     }
441 }