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