View Javadoc

1   package com.atlassian.vcache.internal.test;
2   
3   import com.atlassian.marshalling.api.MarshallingPair;
4   import com.atlassian.marshalling.jdk.StringMarshalling;
5   import com.atlassian.vcache.ChangeRate;
6   import com.atlassian.vcache.DirectExternalCache;
7   import com.atlassian.vcache.ExternalCacheException;
8   import com.atlassian.vcache.ExternalCacheSettings;
9   import com.atlassian.vcache.ExternalCacheSettingsBuilder;
10  import com.atlassian.vcache.JvmCache;
11  import com.atlassian.vcache.JvmCacheSettings;
12  import com.atlassian.vcache.JvmCacheSettingsBuilder;
13  import com.atlassian.vcache.RequestCache;
14  import com.atlassian.vcache.StableReadExternalCache;
15  import com.atlassian.vcache.TransactionalExternalCache;
16  import com.atlassian.vcache.VCacheFactory;
17  import com.atlassian.vcache.VCacheUtils;
18  import com.atlassian.vcache.internal.BegunTransactionalActivityHandler;
19  import com.atlassian.vcache.internal.ExternalCacheDetails;
20  import com.atlassian.vcache.internal.JvmCacheDetails;
21  import com.atlassian.vcache.internal.LongMetric;
22  import com.atlassian.vcache.internal.MetricLabel;
23  import com.atlassian.vcache.internal.RequestCacheDetails;
24  import com.atlassian.vcache.internal.RequestContext;
25  import com.atlassian.vcache.internal.RequestMetrics;
26  import com.atlassian.vcache.internal.VCacheLifecycleManager;
27  import com.atlassian.vcache.internal.VCacheManagement;
28  import com.atlassian.vcache.marshallers.MarshallerFactory;
29  import org.junit.Rule;
30  import org.junit.Test;
31  import org.junit.rules.ExpectedException;
32  import org.slf4j.Logger;
33  import org.slf4j.LoggerFactory;
34  
35  import java.time.Duration;
36  import java.util.EnumMap;
37  import java.util.Map;
38  import java.util.Optional;
39  import java.util.concurrent.CompletionStage;
40  
41  import static com.atlassian.vcache.PutPolicy.PUT_ALWAYS;
42  import static com.atlassian.vcache.VCacheUtils.unsafeJoin;
43  import static com.atlassian.vcache.internal.test.CompletionStageSuccessful.successful;
44  import static com.atlassian.vcache.internal.test.CompletionStageSuccessful.successfulWith;
45  import static org.hamcrest.Matchers.containsInAnyOrder;
46  import static org.hamcrest.Matchers.greaterThan;
47  import static org.hamcrest.Matchers.is;
48  import static org.hamcrest.Matchers.notNullValue;
49  import static org.junit.Assert.assertThat;
50  
51  /**
52   * Base test class for the VCacheService.
53   */
54  @SuppressWarnings("OptionalGetWithoutIsPresent")
55  public abstract class AbstractVCacheServiceIT {
56      protected static final int MAX_ENTRIES = 100;
57      protected static final Duration MAX_TTL = Duration.ofSeconds(60);
58  
59      private static final Logger log = LoggerFactory.getLogger(AbstractVCacheServiceIT.class);
60  
61      @Rule
62      public ExpectedException thrown = ExpectedException.none();
63  
64      @Rule
65      public LoggingTestWatcher watcher = new LoggingTestWatcher(log);
66  
67      @SuppressWarnings("WeakerAccess")
68      protected int invocationsOfBegunTxns;
69      protected final BegunTransactionalActivityHandler begunTxnActivityHandler = c -> invocationsOfBegunTxns++;
70  
71      protected abstract VCacheFactory vCacheFactory();
72  
73      protected abstract VCacheManagement vCacheManagement();
74  
75      protected abstract VCacheLifecycleManager vCacheLifecycleManager();
76  
77      protected abstract RequestContext currentRequestContext();
78  
79      protected abstract void forceNewRequestContext();
80  
81      @Test
82      public void jvmCache_normal() {
83          final JvmCacheSettings settings = new JvmCacheSettingsBuilder()
84                  .defaultTtl(MAX_TTL.plusSeconds(1))
85                  .maxEntries(MAX_ENTRIES + 1)
86                  .build();
87  
88          final JvmCache<String, Long> cache = vCacheFactory().getJvmCache("my-jvm-cache", settings);
89  
90          assertThat(cache, notNullValue());
91          assertThat(cache.getName(), is("my-jvm-cache"));
92  
93          cache.put("key", 666L);
94  
95          final RequestMetrics metrics = vCacheLifecycleManager().metrics(currentRequestContext());
96  
97          assertThat(metrics, notNullValue());
98          assertThat(metrics.allJvmCacheLongMetrics(), notNullValue());
99  
100         final EnumMap<MetricLabel, ? extends LongMetric> myCacheMetrics =
101                 metrics.allJvmCacheLongMetrics().get("my-jvm-cache");
102 
103         assertThat(myCacheMetrics, notNullValue());
104         final LongMetric timedPuts = myCacheMetrics.get(MetricLabel.TIMED_PUT_CALL);
105         assertThat(timedPuts, notNullValue());
106         assertThat(timedPuts.getSampleCount(), is(1L));
107         assertThat(timedPuts.getSamplesTotal(), greaterThan(0L));
108         assertThat(vCacheManagement().allExternalCacheDetails().size(), is(0));
109         assertThat(vCacheManagement().allRequestCacheDetails().size(), is(0));
110 
111         final Map<String, JvmCacheDetails> allCacheDetails = vCacheManagement().allJvmCacheDetails();
112 
113         assertThat(allCacheDetails, notNullValue());
114         assertThat(allCacheDetails.keySet(), containsInAnyOrder("my-jvm-cache"));
115 
116         final JvmCacheDetails cacheDetails = allCacheDetails.get("my-jvm-cache");
117 
118         assertThat(cacheDetails.getName(), is("my-jvm-cache"));
119         assertThat(cacheDetails.getSettings().getDefaultTtl(), is(Optional.of(MAX_TTL)));
120         assertThat(cacheDetails.getSettings().getMaxEntries(), is(Optional.of(MAX_ENTRIES)));
121 
122         assertThat(invocationsOfBegunTxns, is(0));
123     }
124 
125     @Test
126     public void requestCache_normal() {
127         final RequestCache<String, String> cache = vCacheFactory().getRequestCache("my-request-cache");
128 
129         assertThat(cache, notNullValue());
130         assertThat(cache.getName(), is("my-request-cache"));
131 
132         cache.removeAll();
133         cache.put("one", "eine");
134 
135         final Optional<String> get1 = cache.get("one");
136 
137         assertThat(get1, is(Optional.of("eine")));
138         assertThat(vCacheManagement().allJvmCacheDetails().size(), is(0));
139         assertThat(vCacheManagement().allExternalCacheDetails().size(), is(0));
140 
141         final Map<String, RequestCacheDetails> allCacheDetails = vCacheManagement().allRequestCacheDetails();
142 
143         assertThat(allCacheDetails, notNullValue());
144         assertThat(allCacheDetails.keySet(), containsInAnyOrder("my-request-cache"));
145 
146         final RequestCacheDetails cacheDetails = allCacheDetails.get("my-request-cache");
147 
148         assertThat(cacheDetails.getName(), is("my-request-cache"));
149         assertThat(invocationsOfBegunTxns, is(0));
150     }
151 
152     @Test
153     public void directExternalCache_normal_marshalling() {
154         final ExternalCacheSettings settings = new ExternalCacheSettingsBuilder()
155                 .defaultTtl(Duration.ofSeconds(60))
156                 .entryGrowthRateHint(ChangeRate.LOW_CHANGE)
157                 .dataChangeRateHint(ChangeRate.LOW_CHANGE)
158                 .entryCountHint(100)
159                 .build();
160 
161         final DirectExternalCache<String> cache = vCacheFactory().getDirectExternalCache(
162                 "my-direct-cache", StringMarshalling.pair(), settings);
163 
164         assertThat(cache, notNullValue());
165         assertThat(cache.getName(), is("my-direct-cache"));
166 
167         final CompletionStage<Void> rmall = cache.removeAll();
168 
169         assertThat(rmall, successful());
170 
171         final CompletionStage<Boolean> put1 = cache.put("one", "eine", PUT_ALWAYS);
172 
173         assertThat(put1, successfulWith(is(true)));
174 
175         final CompletionStage<Optional<String>> get1 = cache.get("one");
176 
177         assertThat(get1, successfulWith(is(Optional.of("eine"))));
178         assertThat(vCacheManagement().allJvmCacheDetails().size(), is(0));
179         assertThat(vCacheManagement().allRequestCacheDetails().size(), is(0));
180 
181         final Map<String, ExternalCacheDetails> allCacheDetails = vCacheManagement().allExternalCacheDetails();
182 
183         assertThat(allCacheDetails, notNullValue());
184         assertThat(allCacheDetails.keySet(), containsInAnyOrder("my-direct-cache"));
185 
186         final ExternalCacheDetails cacheDetails = allCacheDetails.get("my-direct-cache");
187 
188         assertThat(cacheDetails.getName(), is("my-direct-cache"));
189         assertThat(cacheDetails.getSettings().getDefaultTtl(), is(Optional.of(MAX_TTL)));
190         assertThat(cacheDetails.getSettings().getEntryCountHint(), is(Optional.of(MAX_ENTRIES)));
191         assertThat(invocationsOfBegunTxns, is(0));
192     }
193 
194     @Test
195     public void directExternalCache_normal_marshaller() {
196         final ExternalCacheSettings settings = new ExternalCacheSettingsBuilder()
197                 .defaultTtl(Duration.ofSeconds(60))
198                 .entryGrowthRateHint(ChangeRate.LOW_CHANGE)
199                 .dataChangeRateHint(ChangeRate.LOW_CHANGE)
200                 .entryCountHint(100)
201                 .build();
202 
203         @SuppressWarnings("deprecation")
204         final DirectExternalCache<String> cache = vCacheFactory().getDirectExternalCache(
205                 "my-direct-cache", MarshallerFactory.stringMarshaller(), settings);
206 
207         assertThat(cache, notNullValue());
208         assertThat(cache.getName(), is("my-direct-cache"));
209 
210         final CompletionStage<Void> rmall = cache.removeAll();
211 
212         assertThat(rmall, successful());
213 
214         final CompletionStage<Boolean> put1 = cache.put("one1", "eine", PUT_ALWAYS);
215 
216         assertThat(put1, successfulWith(is(true)));
217 
218         final CompletionStage<Optional<String>> get1 = cache.get("one1");
219 
220         assertThat(get1, successfulWith(is(Optional.of("eine"))));
221         assertThat(vCacheManagement().allJvmCacheDetails().size(), is(0));
222         assertThat(vCacheManagement().allRequestCacheDetails().size(), is(0));
223     }
224 
225     @Test
226     public void stableReadExternalCache_normal_marshalling() {
227         final ExternalCacheSettings settings = new ExternalCacheSettingsBuilder()
228                 .defaultTtl(Duration.ofSeconds(60))
229                 .entryGrowthRateHint(ChangeRate.LOW_CHANGE)
230                 .dataChangeRateHint(ChangeRate.LOW_CHANGE)
231                 .entryCountHint(100)
232                 .build();
233 
234         final StableReadExternalCache<String> cache = vCacheFactory().getStableReadExternalCache(
235                 "my-stable-read-cache", StringMarshalling.pair(), settings);
236 
237         assertThat(cache, notNullValue());
238         assertThat(cache.getName(), is("my-stable-read-cache"));
239 
240         final CompletionStage<Void> rmall = cache.removeAll();
241 
242         assertThat(rmall, successful());
243 
244         final CompletionStage<Boolean> put1 = cache.put("three", "drei", PUT_ALWAYS);
245 
246         assertThat(put1, successfulWith(is(true)));
247 
248         final CompletionStage<Optional<String>> get1 = cache.get("three");
249 
250         assertThat(get1, successfulWith(is(Optional.of("drei"))));
251         assertThat(invocationsOfBegunTxns, is(0));
252     }
253 
254     @Test
255     public void stableReadExternalCache_normal_marshaller() {
256         final ExternalCacheSettings settings = new ExternalCacheSettingsBuilder()
257                 .defaultTtl(Duration.ofSeconds(60))
258                 .entryGrowthRateHint(ChangeRate.LOW_CHANGE)
259                 .dataChangeRateHint(ChangeRate.LOW_CHANGE)
260                 .entryCountHint(100)
261                 .build();
262 
263         @SuppressWarnings("deprecation")
264         final StableReadExternalCache<String> cache = vCacheFactory().getStableReadExternalCache(
265                 "my-stable-read-cache", MarshallerFactory.stringMarshaller(), settings);
266 
267         assertThat(cache, notNullValue());
268         assertThat(cache.getName(), is("my-stable-read-cache"));
269 
270         final CompletionStage<Void> rmall = cache.removeAll();
271 
272         assertThat(rmall, successful());
273 
274         final CompletionStage<Boolean> put1 = cache.put("three", "drei", PUT_ALWAYS);
275 
276         assertThat(put1, successfulWith(is(true)));
277 
278         final CompletionStage<Optional<String>> get1 = cache.get("three");
279 
280         assertThat(get1, successfulWith(is(Optional.of("drei"))));
281         assertThat(invocationsOfBegunTxns, is(0));
282     }
283 
284     @Test
285     public void transactionalExternalCache_normal_marshalling() {
286         final ExternalCacheSettings settings = new ExternalCacheSettingsBuilder()
287                 .defaultTtl(Duration.ofSeconds(60))
288                 .entryGrowthRateHint(ChangeRate.LOW_CHANGE)
289                 .dataChangeRateHint(ChangeRate.LOW_CHANGE)
290                 .entryCountHint(100)
291                 .build();
292 
293         final TransactionalExternalCache<String> cache = vCacheFactory().getTransactionalExternalCache(
294                 "my-txn-cache", StringMarshalling.pair(), settings);
295 
296         assertThat(cache, notNullValue());
297         assertThat(cache.getName(), is("my-txn-cache"));
298         assertThat(invocationsOfBegunTxns, is(0));
299 
300         cache.removeAll();
301         assertThat(invocationsOfBegunTxns, is(1));
302 
303         cache.put("three", "drei", PUT_ALWAYS);
304 
305         final CompletionStage<Optional<String>> get1 = cache.get("three");
306 
307         assertThat(get1, successfulWith(is(Optional.of("drei"))));
308 
309         vCacheLifecycleManager().transactionSync(currentRequestContext());
310         assertThat(invocationsOfBegunTxns, is(1));
311 
312         forceNewRequestContext();
313         final CompletionStage<Optional<String>> get2 = cache.get("three");
314         assertThat(invocationsOfBegunTxns, is(2));
315         unsafeJoin(get2.toCompletableFuture()).get();
316         assertThat(get2, successfulWith(is(Optional.of("drei"))));
317         assertThat(invocationsOfBegunTxns, is(2));
318     }
319 
320     @Test
321     public void transactionalExternalCache_normal_marshaller() {
322         final ExternalCacheSettings settings = new ExternalCacheSettingsBuilder()
323                 .defaultTtl(Duration.ofSeconds(60))
324                 .entryGrowthRateHint(ChangeRate.LOW_CHANGE)
325                 .dataChangeRateHint(ChangeRate.LOW_CHANGE)
326                 .entryCountHint(100)
327                 .build();
328 
329         @SuppressWarnings("deprecation")
330         final TransactionalExternalCache<String> cache = vCacheFactory().getTransactionalExternalCache(
331                 "my-txn-cache", MarshallerFactory.stringMarshaller(), settings);
332 
333         assertThat(cache, notNullValue());
334         assertThat(cache.getName(), is("my-txn-cache"));
335         assertThat(invocationsOfBegunTxns, is(0));
336 
337         cache.removeAll();
338         assertThat(invocationsOfBegunTxns, is(1));
339 
340         cache.put("three", "drei", PUT_ALWAYS);
341 
342         final CompletionStage<Optional<String>> get1 = cache.get("three");
343 
344         assertThat(get1, successfulWith(is(Optional.of("drei"))));
345 
346         vCacheLifecycleManager().transactionSync(currentRequestContext());
347         assertThat(invocationsOfBegunTxns, is(1));
348 
349         forceNewRequestContext();
350         final CompletionStage<Optional<String>> get2 = cache.get("three");
351         assertThat(invocationsOfBegunTxns, is(2));
352         unsafeJoin(get2.toCompletableFuture()).get();
353         assertThat(get2, successfulWith(is(Optional.of("drei"))));
354         assertThat(invocationsOfBegunTxns, is(2));
355     }
356 
357     @Test
358     public void verify_txns_sync_sync_sync() {
359         final ExternalCacheSettings settings = new ExternalCacheSettingsBuilder()
360                 .defaultTtl(Duration.ofSeconds(60))
361                 .entryGrowthRateHint(ChangeRate.LOW_CHANGE)
362                 .dataChangeRateHint(ChangeRate.LOW_CHANGE)
363                 .entryCountHint(100)
364                 .build();
365 
366         final TransactionalExternalCache<String> cache1 = vCacheFactory().getTransactionalExternalCache(
367                 "sync_sync_sync-1", StringMarshalling.pair(), settings);
368         assertThat(invocationsOfBegunTxns, is(0));
369 
370         cache1.put("updated-key", "first1", PUT_ALWAYS);
371         assertThat(invocationsOfBegunTxns, is(1));
372 
373         cache1.put("phase-1", "first", PUT_ALWAYS);
374         vCacheLifecycleManager().transactionSync(currentRequestContext());
375         assertThat(invocationsOfBegunTxns, is(1));
376 
377         final TransactionalExternalCache<String> cache2 = vCacheFactory().getTransactionalExternalCache(
378                 "sync_sync_sync-2", StringMarshalling.pair(), settings);
379 
380         cache1.put("updated-key", "second1", PUT_ALWAYS);
381         assertThat(invocationsOfBegunTxns, is(2));
382 
383         cache2.put("updated-key", "second2", PUT_ALWAYS);
384         cache1.put("phase-2", "second", PUT_ALWAYS);
385         cache2.put("phase-2", "second", PUT_ALWAYS);
386 
387         vCacheLifecycleManager().transactionSync(currentRequestContext());
388         assertThat(invocationsOfBegunTxns, is(2));
389 
390         final TransactionalExternalCache<String> cache3 = vCacheFactory().getTransactionalExternalCache(
391                 "sync_sync_sync-3", StringMarshalling.pair(), settings);
392 
393         cache1.put("updated-key", "third1", PUT_ALWAYS);
394         assertThat(invocationsOfBegunTxns, is(3));
395         cache2.put("updated-key", "third2", PUT_ALWAYS);
396         cache1.put("phase-3", "third", PUT_ALWAYS);
397         cache2.put("phase-3", "third", PUT_ALWAYS);
398         cache3.put("phase-3", "third", PUT_ALWAYS);
399 
400         vCacheLifecycleManager().transactionSync(currentRequestContext());
401         assertThat(invocationsOfBegunTxns, is(3));
402 
403         forceNewRequestContext();
404 
405         assertThat(cache1.get("updated-key"), successfulWith(is(Optional.of("third1"))));
406         assertThat(invocationsOfBegunTxns, is(4));
407         assertThat(unsafeJoin(cache1.get("updated-key")), is(Optional.of("third1")));
408         assertThat(unsafeJoin(cache1.get("phase-1")), is(Optional.of("first")));
409         assertThat(unsafeJoin(cache1.get("phase-2")), is(Optional.of("second")));
410         assertThat(unsafeJoin(cache1.get("phase-3")), is(Optional.of("third")));
411 
412         assertThat(unsafeJoin(cache2.get("updated-key")), is(Optional.of("third2")));
413         assertThat(unsafeJoin(cache2.get("phase-2")), is(Optional.of("second")));
414         assertThat(unsafeJoin(cache2.get("phase-3")), is(Optional.of("third")));
415 
416         assertThat(unsafeJoin(cache3.get("phase-3")), is(Optional.of("third")));
417         assertThat(invocationsOfBegunTxns, is(4));
418     }
419 
420     @Test
421     public void verify_txns_sync_discard_sync() {
422         final ExternalCacheSettings settings = new ExternalCacheSettingsBuilder()
423                 .defaultTtl(Duration.ofSeconds(60))
424                 .entryGrowthRateHint(ChangeRate.LOW_CHANGE)
425                 .dataChangeRateHint(ChangeRate.LOW_CHANGE)
426                 .entryCountHint(100)
427                 .build();
428 
429         final TransactionalExternalCache<String> cache1 = vCacheFactory().getTransactionalExternalCache(
430                 "sync_discard_sync-1", StringMarshalling.pair(), settings);
431         assertThat(invocationsOfBegunTxns, is(0));
432 
433         cache1.put("updated-key", "first1", PUT_ALWAYS);
434         assertThat(invocationsOfBegunTxns, is(1));
435         cache1.put("phase-1", "first", PUT_ALWAYS);
436 
437         vCacheLifecycleManager().transactionSync(currentRequestContext());
438 
439         final TransactionalExternalCache<String> cache2 = vCacheFactory().getTransactionalExternalCache(
440                 "sync_discard_sync-2", StringMarshalling.pair(), settings);
441 
442         assertThat(invocationsOfBegunTxns, is(1));
443         cache1.put("updated-key", "second1", PUT_ALWAYS);
444         assertThat(invocationsOfBegunTxns, is(2));
445         cache2.put("updated-key", "second2", PUT_ALWAYS);
446         cache1.put("phase-2", "second", PUT_ALWAYS);
447         cache2.put("phase-2", "second", PUT_ALWAYS);
448 
449         vCacheLifecycleManager().transactionDiscard(currentRequestContext());
450 
451         final TransactionalExternalCache<String> cache3 = vCacheFactory().getTransactionalExternalCache(
452                 "sync_discard_sync-3", StringMarshalling.pair(), settings);
453 
454         assertThat(invocationsOfBegunTxns, is(2));
455         cache1.put("updated-key", "third1", PUT_ALWAYS);
456         assertThat(invocationsOfBegunTxns, is(3));
457         cache2.put("updated-key", "third2", PUT_ALWAYS);
458         cache1.put("phase-3", "third", PUT_ALWAYS);
459         cache2.put("phase-3", "third", PUT_ALWAYS);
460         cache3.put("phase-3", "third", PUT_ALWAYS);
461 
462         vCacheLifecycleManager().transactionSync(currentRequestContext());
463 
464         forceNewRequestContext();
465 
466         assertThat(invocationsOfBegunTxns, is(3));
467         assertThat(unsafeJoin(cache1.get("updated-key")), is(Optional.of("third1")));
468         assertThat(invocationsOfBegunTxns, is(4));
469         assertThat(unsafeJoin(cache1.get("phase-1")), is(Optional.of("first")));
470         assertThat(unsafeJoin(cache1.get("phase-2")), is(Optional.empty()));
471         assertThat(unsafeJoin(cache1.get("phase-3")), is(Optional.of("third")));
472 
473         assertThat(unsafeJoin(cache2.get("updated-key")), is(Optional.of("third2")));
474         assertThat(unsafeJoin(cache2.get("phase-2")), is(Optional.empty()));
475         assertThat(unsafeJoin(cache2.get("phase-3")), is(Optional.of("third")));
476 
477         assertThat(unsafeJoin(cache3.get("phase-3")), is(Optional.of("third")));
478         assertThat(invocationsOfBegunTxns, is(4));
479     }
480 
481     @Test
482     public void verify_txns_discard_sync_discard() {
483         final ExternalCacheSettings settings = new ExternalCacheSettingsBuilder()
484                 .defaultTtl(Duration.ofSeconds(60))
485                 .entryGrowthRateHint(ChangeRate.LOW_CHANGE)
486                 .dataChangeRateHint(ChangeRate.LOW_CHANGE)
487                 .entryCountHint(100)
488                 .build();
489 
490         final TransactionalExternalCache<String> cache1 = vCacheFactory().getTransactionalExternalCache(
491                 "discard_sync_discard-1", StringMarshalling.pair(), settings);
492 
493         cache1.put("updated-key", "first1", PUT_ALWAYS);
494         cache1.put("phase-1", "first", PUT_ALWAYS);
495 
496         vCacheLifecycleManager().transactionDiscard(currentRequestContext());
497 
498         final TransactionalExternalCache<String> cache2 = vCacheFactory().getTransactionalExternalCache(
499                 "discard_sync_discard-2", StringMarshalling.pair(), settings);
500 
501         cache1.put("updated-key", "second1", PUT_ALWAYS);
502         cache2.put("updated-key", "second2", PUT_ALWAYS);
503         cache1.put("phase-2", "second", PUT_ALWAYS);
504         cache2.put("phase-2", "second", PUT_ALWAYS);
505 
506         vCacheLifecycleManager().transactionSync(currentRequestContext());
507 
508         final TransactionalExternalCache<String> cache3 = vCacheFactory().getTransactionalExternalCache(
509                 "discard_sync_discard-3", StringMarshalling.pair(), settings);
510 
511         cache1.put("updated-key", "third1", PUT_ALWAYS);
512         cache2.put("updated-key", "third2", PUT_ALWAYS);
513         cache1.put("phase-3", "third", PUT_ALWAYS);
514         cache2.put("phase-3", "third", PUT_ALWAYS);
515         cache3.put("phase-3", "third", PUT_ALWAYS);
516 
517 
518         vCacheLifecycleManager().transactionDiscard(currentRequestContext());
519 
520         forceNewRequestContext();
521 
522         assertThat(unsafeJoin(cache1.get("updated-key")), is(Optional.of("second1")));
523         assertThat(unsafeJoin(cache1.get("phase-1")), is(Optional.empty()));
524         assertThat(unsafeJoin(cache1.get("phase-2")), is(Optional.of("second")));
525         assertThat(unsafeJoin(cache1.get("phase-3")), is(Optional.empty()));
526 
527         assertThat(unsafeJoin(cache2.get("updated-key")), is(Optional.of("second2")));
528         assertThat(unsafeJoin(cache2.get("phase-2")), is(Optional.of("second")));
529         assertThat(unsafeJoin(cache2.get("phase-3")), is(Optional.empty()));
530 
531         assertThat(unsafeJoin(cache3.get("phase-3")), is(Optional.empty()));
532         assertThat(invocationsOfBegunTxns, is(4));
533     }
534 
535     @Test
536     public void verify_txns_discard_discard_discard() {
537         final ExternalCacheSettings settings = new ExternalCacheSettingsBuilder()
538                 .defaultTtl(Duration.ofSeconds(60))
539                 .entryGrowthRateHint(ChangeRate.LOW_CHANGE)
540                 .dataChangeRateHint(ChangeRate.LOW_CHANGE)
541                 .entryCountHint(100)
542                 .build();
543 
544         final TransactionalExternalCache<String> cache1 = vCacheFactory().getTransactionalExternalCache(
545                 "discard_discard_discard-1", StringMarshalling.pair(), settings);
546 
547         cache1.put("updated-key", "first1", PUT_ALWAYS);
548         cache1.put("phase-1", "first", PUT_ALWAYS);
549 
550         vCacheLifecycleManager().transactionDiscard(currentRequestContext());
551 
552         final TransactionalExternalCache<String> cache2 = vCacheFactory().getTransactionalExternalCache(
553                 "discard_discard_discard-2", StringMarshalling.pair(), settings);
554 
555         cache1.put("updated-key", "second1", PUT_ALWAYS);
556         cache2.put("updated-key", "second2", PUT_ALWAYS);
557         cache1.put("phase-2", "second", PUT_ALWAYS);
558         cache2.put("phase-2", "second", PUT_ALWAYS);
559 
560         vCacheLifecycleManager().transactionDiscard(currentRequestContext());
561 
562         final TransactionalExternalCache<String> cache3 = vCacheFactory().getTransactionalExternalCache(
563                 "discard_discard_discard-3", StringMarshalling.pair(), settings);
564 
565         cache1.put("updated-key", "third1", PUT_ALWAYS);
566         cache2.put("updated-key", "third2", PUT_ALWAYS);
567         cache1.put("phase-3", "third", PUT_ALWAYS);
568         cache2.put("phase-3", "third", PUT_ALWAYS);
569         cache3.put("phase-3", "third", PUT_ALWAYS);
570 
571         vCacheLifecycleManager().transactionDiscard(currentRequestContext());
572 
573         forceNewRequestContext();
574 
575         assertThat(unsafeJoin(cache1.get("updated-key")), is(Optional.empty()));
576         assertThat(unsafeJoin(cache1.get("phase-1")), is(Optional.empty()));
577         assertThat(unsafeJoin(cache1.get("phase-2")), is(Optional.empty()));
578         assertThat(unsafeJoin(cache1.get("phase-3")), is(Optional.empty()));
579 
580         assertThat(unsafeJoin(cache2.get("updated-key")), is(Optional.empty()));
581         assertThat(unsafeJoin(cache2.get("phase-2")), is(Optional.empty()));
582         assertThat(unsafeJoin(cache2.get("phase-3")), is(Optional.empty()));
583 
584         assertThat(unsafeJoin(cache3.get("phase-3")), is(Optional.empty()));
585         assertThat(invocationsOfBegunTxns, is(4));
586     }
587 
588     @Test
589     public void duplicate_names_different_policys() {
590         final DirectExternalCache<String> directCache =
591                 vCacheFactory().getDirectExternalCache(
592                         "duplicate", StringMarshalling.pair(), new ExternalCacheSettingsBuilder().build());
593 
594         thrown.expect(ExternalCacheException.class);
595         thrown.expectMessage("Failed due to CREATION_FAILURE");
596 
597         final StableReadExternalCache<String> stableCache =
598                 vCacheFactory().getStableReadExternalCache(
599                         "duplicate", StringMarshalling.pair(), new ExternalCacheSettingsBuilder().build());
600     }
601 
602     @Test
603     public void directexternalcache_duplicate_obtain() {
604         final DirectExternalCache<String> firstTime =
605                 vCacheFactory().getDirectExternalCache(
606                         "duplicate", dodgyPair("first"), new ExternalCacheSettingsBuilder().build());
607 
608         final CompletionStage<Boolean> put1 = firstTime.put("key", "ignored", PUT_ALWAYS);
609         assertThat(VCacheUtils.unsafeJoin(put1), is(true));
610 
611         final CompletionStage<Optional<String>> get1 = firstTime.get("key");
612         assertThat(VCacheUtils.unsafeJoin(get1), is(Optional.of("first")));
613 
614         final DirectExternalCache<String> secondTime =
615                 vCacheFactory().getDirectExternalCache(
616                         "duplicate", dodgyPair("second"), new ExternalCacheSettingsBuilder().build());
617 
618         final CompletionStage<Optional<String>> get2 = secondTime.get("key");
619         assertThat(VCacheUtils.unsafeJoin(get2), is(Optional.of("second")));
620     }
621 
622     @Test
623     public void stablereadexternalcache_duplicate_obtain() {
624         final StableReadExternalCache<String> firstTime =
625                 vCacheFactory().getStableReadExternalCache(
626                         "duplicate", dodgyPair("first"), new ExternalCacheSettingsBuilder().build());
627 
628         final CompletionStage<Boolean> put1 = firstTime.put("key", "ignored", PUT_ALWAYS);
629         assertThat(VCacheUtils.unsafeJoin(put1), is(true));
630 
631         forceNewRequestContext();
632         final CompletionStage<Optional<String>> get1 = firstTime.get("key");
633         assertThat(VCacheUtils.unsafeJoin(get1), is(Optional.of("first")));
634 
635         final StableReadExternalCache<String> secondTime =
636                 vCacheFactory().getStableReadExternalCache(
637                         "duplicate", dodgyPair("second"), new ExternalCacheSettingsBuilder().build());
638         forceNewRequestContext();
639 
640         final CompletionStage<Optional<String>> get2 = secondTime.get("key");
641         assertThat(VCacheUtils.unsafeJoin(get2), is(Optional.of("second")));
642     }
643 
644     @Test
645     public void txnexternalcache_duplicate_obtain() {
646         final TransactionalExternalCache<String> firstTime =
647                 vCacheFactory().getTransactionalExternalCache(
648                         "duplicate", dodgyPair("first"), new ExternalCacheSettingsBuilder().build());
649 
650         firstTime.put("key", "ignored", PUT_ALWAYS);
651         vCacheLifecycleManager().transactionSync(currentRequestContext());
652 
653         forceNewRequestContext();
654         final CompletionStage<Optional<String>> get1 = firstTime.get("key");
655         assertThat(VCacheUtils.unsafeJoin(get1), is(Optional.of("first")));
656 
657         final TransactionalExternalCache<String> secondTime =
658                 vCacheFactory().getTransactionalExternalCache(
659                         "duplicate", dodgyPair("second"), new ExternalCacheSettingsBuilder().build());
660 
661         forceNewRequestContext();
662         final CompletionStage<Optional<String>> get2 = secondTime.get("key");
663         assertThat(VCacheUtils.unsafeJoin(get2), is(Optional.of("second")));
664     }
665 
666     private static MarshallingPair<String> dodgyPair(String returning) {
667         return new MarshallingPair<>(String::getBytes, b -> returning);
668     }
669 
670     @Test
671     public void handle_legal_recursive_get_supplier() {
672         final ExternalCacheSettings settings = new ExternalCacheSettingsBuilder()
673                 .defaultTtl(Duration.ofSeconds(60))
674                 .entryGrowthRateHint(ChangeRate.LOW_CHANGE)
675                 .dataChangeRateHint(ChangeRate.LOW_CHANGE)
676                 .entryCountHint(100)
677                 .build();
678 
679         final StableReadExternalCache<String> cache = vCacheFactory().getStableReadExternalCache(
680                 "my-stable-read-cache", StringMarshalling.pair(), settings);
681 
682         final CompletionStage<String> get1 = cache.get("recursive", () ->
683                 "ignored-" + unsafeJoin(cache.get("recursive", () -> "2")));
684         assertThat(get1, successfulWith(is("2")));
685     }
686 
687     @Test
688     public void handle_legal_recursive_get_supplier2() {
689         final ExternalCacheSettings settings = new ExternalCacheSettingsBuilder()
690                 .defaultTtl(Duration.ofSeconds(60))
691                 .entryGrowthRateHint(ChangeRate.LOW_CHANGE)
692                 .dataChangeRateHint(ChangeRate.LOW_CHANGE)
693                 .entryCountHint(100)
694                 .build();
695 
696         final StableReadExternalCache<String> cache = vCacheFactory().getStableReadExternalCache(
697                 "my-stable-read-cache", StringMarshalling.pair(), settings);
698 
699         final CompletionStage<String> get1 = cache.get("recursive", () ->
700                 unsafeJoin(cache.get("recursive2", () -> "pass")));
701         assertThat(get1, successfulWith(is("pass")));
702     }
703 }