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
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
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
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
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 }