View Javadoc

1   package com.atlassian.cache;
2   
3   import java.util.concurrent.CountDownLatch;
4   import java.util.concurrent.TimeUnit;
5   import java.util.concurrent.atomic.AtomicInteger;
6   
7   import javax.annotation.Nonnull;
8   import javax.annotation.Nullable;
9   
10  import com.google.common.base.Function;
11  
12  import org.junit.Test;
13  
14  import static com.atlassian.cache.TestThread.runTest;
15  import static org.hamcrest.Matchers.is;
16  import static org.hamcrest.Matchers.isOneOf;
17  import static org.hamcrest.core.IsEqual.equalTo;
18  import static org.hamcrest.core.IsInstanceOf.instanceOf;
19  import static org.junit.Assert.assertNotSame;
20  import static org.junit.Assert.assertThat;
21  import static org.junit.Assert.fail;
22  
23  /**
24   * Test the Lazy Cache
25   *
26   * @since 2.0
27   */
28  public abstract class AbstractCacheLazyTest extends AbstractCacheTest
29  {
30  
31      @Test
32      public void testGetName() throws Exception
33      {
34          Cache<String, Long> cache = makeUnexpiringCache();
35  
36          assertThat(cache.getName(), equalTo("mycache"));
37      }
38  
39      @Test
40      public void testFactoryGeneratedName() throws Exception
41      {
42          Cache<String, Long> cache = factory.getCache(Object.class, "mycache");
43  
44          assertThat(cache.getName(), equalTo("java.lang.Object.mycache"));
45      }
46  
47      @Test
48      public void testGetKeys() throws Exception
49      {
50          Cache<String, Long> cache = makeUnexpiringCache();
51          // Add some entries using the builder
52          cache.get("1");
53          cache.get("2");
54          cache.get("3");
55          cache.get("4");
56          assertThat(cache.get("1"), equalTo(1L));
57          assertThat(cache.get("2"), equalTo(2L));
58          assertThat(cache.get("3"), equalTo(3L));
59          assertThat(cache.get("4"), equalTo(4L));
60          assertSize(cache, 4);
61      }
62  
63      @Test
64      public void testConstructExpiringCache() throws Exception
65      {
66          Cache<String, Long> cache = makeExpiringCache();
67          // Add some entries using the builder
68          cache.get("1");
69          cache.get("2");
70          cache.get("3");
71          cache.get("4");
72          assertThat(cache.get("1"), equalTo(1L));
73          assertThat(cache.get("2"), equalTo(2L));
74          assertThat(cache.get("3"), equalTo(3L));
75          assertThat(cache.get("4"), equalTo(4L));
76          assertSize(cache, 4);
77      }
78  
79      @Test
80      public void testPut() throws Exception
81      {
82          Cache<String, Long> cache = makeUnexpiringCache();
83          // Add some entries using the builder
84          cache.put("1", 11L);
85          cache.put("2", 12L);
86          cache.put("3", 13L);
87          cache.put("4", 14L);
88          assertThat(cache.get("1"), equalTo(11L));
89          assertThat(cache.get("2"), equalTo(12L));
90          assertThat(cache.get("3"), equalTo(13L));
91          assertThat(cache.get("4"), equalTo(14L));
92          assertSize(cache, (4));
93      }
94  
95      @Test
96      public void testGet() throws Exception
97      {
98          Cache<String, Long> cache = makeUnexpiringCache();
99          // Add some entries using the builder
100         cache.put("1", 11L);
101         cache.put("2", 12L);
102         cache.put("3", 13L);
103         cache.put("4", 14L);
104         assertThat(cache.get("1"), equalTo(11L));
105         assertThat(cache.get("2"), equalTo(12L));
106         assertThat(cache.get("3"), equalTo(13L));
107         assertThat(cache.get("4"), equalTo(14L));
108         assertThat(cache.get("5"), equalTo(5L));
109         assertThat(cache.get("6"), equalTo(6L));
110         assertThat(cache.get("7"), equalTo(7L));
111         assertSize(cache, (7));
112     }
113 
114     @Test
115     public void testRemove() throws Exception
116     {
117         Cache<String, Long> cache = makeUnexpiringCache();
118         // Add some entries using the builder
119         cache.put("1", 11L);
120         cache.put("2", 12L);
121         cache.put("3", 13L);
122         cache.put("4", 14L);
123         assertThat(cache.get("1"), equalTo(11L));
124         assertThat(cache.get("2"), equalTo(12L));
125         assertThat(cache.get("3"), equalTo(13L));
126         assertThat(cache.get("4"), equalTo(14L));
127         assertSize(cache, (4));
128 
129         cache.remove("1");
130         cache.remove("2");
131         cache.remove("3");
132         assertSize(cache, (1));
133 
134         // Removed values should be recomputed
135         assertThat(cache.get("1"), equalTo(1L));
136         assertThat(cache.get("2"), equalTo(2L));
137         assertThat(cache.get("3"), equalTo(3L));
138         assertThat(cache.get("4"), equalTo(14L));
139         assertSize(cache, (4));
140     }
141 
142     @Test
143     public void testRemoveTwice() throws Exception
144     {
145         Cache<String, Long> cache = makeUnexpiringCache();
146 
147         // Add some entries using the builder
148         cache.put("1", 11L);
149         cache.put("2", 12L);
150         cache.put("3", 13L);
151         cache.put("4", 14L);
152         assertThat(cache.get("1"), equalTo(11L));
153         assertThat(cache.get("2"), equalTo(12L));
154         assertThat(cache.get("3"), equalTo(13L));
155         assertThat(cache.get("4"), equalTo(14L));
156         assertSize(cache, (4));
157 
158         cache.remove("1");
159         // Remove again should be a no-op
160         cache.remove("1");
161         assertSize(cache, (3));
162 
163         // Removed values should be recomputed
164         assertThat(cache.get("1"), equalTo(1L));
165         assertThat(cache.get("2"), equalTo(12L));
166         assertThat(cache.get("3"), equalTo(13L));
167         assertThat(cache.get("4"), equalTo(14L));
168         assertSize(cache, (4));
169     }
170 
171     @Test
172     public void testRemoveAll() throws Exception
173     {
174         Cache<String, Long> cache = makeUnexpiringCache();
175         // Add some entries using the builder
176         cache.put("1", 11L);
177         cache.put("2", 12L);
178         cache.put("3", 13L);
179         cache.put("4", 14L);
180         assertThat(cache.get("1"), equalTo(11L));
181         assertThat(cache.get("2"), equalTo(12L));
182         assertThat(cache.get("3"), equalTo(13L));
183         assertThat(cache.get("4"), equalTo(14L));
184         assertSize(cache, (4));
185 
186         cache.removeAll();
187         assertEmpty(cache);
188 
189         // Removed values should be recomputed
190         assertThat(cache.get("1"), equalTo(1L));
191         assertThat(cache.get("2"), equalTo(2L));
192         assertThat(cache.get("3"), equalTo(3L));
193         assertThat(cache.get("4"), equalTo(4L));
194         assertSize(cache, (4));
195     }
196 
197     @Test
198     public void testClear() throws Exception
199     {
200         Cache<String, Long> cache = makeUnexpiringCache();
201         // Add some entries using the builder
202         cache.put("1", 11L);
203         cache.put("2", 12L);
204         cache.put("3", 13L);
205         cache.put("4", 14L);
206         assertThat(cache.get("1"), equalTo(11L));
207         assertThat(cache.get("2"), equalTo(12L));
208         assertThat(cache.get("3"), equalTo(13L));
209         assertThat(cache.get("4"), equalTo(14L));
210         assertSize(cache, (4));
211 
212         ((ManagedCache) cache).clear();
213         assertEmpty(cache);
214 
215         // Removed values should be recomputed
216         assertThat(cache.get("1"), equalTo(1L));
217         assertThat(cache.get("2"), equalTo(2L));
218         assertThat(cache.get("3"), equalTo(3L));
219         assertThat(cache.get("4"), equalTo(4L));
220         assertSize(cache, (4));
221     }
222 
223     @Test (expected = CacheException.class)
224     public void testNullKey() throws Exception
225     {
226         makeNullReturningCache().get(null);
227     }
228 
229     @Test (expected = CacheException.class)
230     public void testNullValue() throws Exception
231     {
232         Cache<String, Long> cache = makeNullReturningCache();
233 
234         // Add some entries using the builder
235         assertThat(cache.get("1"), equalTo(1L));
236         assertThat(cache.get("2"), equalTo(2L));
237         assertThat(cache.get("3"), equalTo(3L));
238 
239         cache.get("George");
240     }
241 
242     @Test
243     public void testMaxEntries() throws Exception
244     {
245         final AtomicInteger loadCounter = new AtomicInteger();
246         Cache<String, Long> cache = makeSizeLimitedCache(3, loadCounter);
247 
248         // Add some entries using the builder
249         assertThat(cache.get("1"), equalTo(1L));
250         assertThat(cache.get("1"), equalTo(1L));
251         assertThat(cache.get("2"), equalTo(2L));
252         assertThat(cache.get("3"), equalTo(3L));
253         assertThat("loadCounter", loadCounter.get(), equalTo(3));
254         assertSize(cache, (3));
255         assertThat(cache.get("3"), equalTo(3L));
256         assertThat(cache.get("3"), equalTo(3L));
257         assertThat("loadCounter", loadCounter.get(), equalTo(3));
258         assertSize(cache, (3));
259         assertThat(cache.get("4"), equalTo(4L));
260         assertThat("loadCounter", loadCounter.get(), equalTo(4));
261         assertSize(cache, (3));
262     }
263 
264     @Test
265     public void testExceptionHandling() throws Exception
266     {
267         Cache<String, Long> cache = makeExceptionalCache();
268 
269         // Add some entries using the builder
270         assertThat(cache.get("1"), equalTo(1L));
271         assertThat(cache.get("2"), equalTo(2L));
272         assertThat(cache.get("3"), equalTo(3L));
273         try
274         {
275             cache.get("George");
276             fail("Should throw CacheException when the loader throws an exception");
277         }
278         catch (CacheException ex)
279         {
280             assertThat("This exception should wrap the original Exception", ex.getCause(), instanceOf(NumberFormatException.class));
281         }
282         try
283         {
284             Long v = cache.get(null);
285             fail("Should throw CacheException when the Key is a null");
286         }
287         catch (CacheException ex)
288         {
289             assertThat("This exception should wrap the original Exception", ex.getCause(), instanceOf(NullPointerException.class));
290         }
291         assertSize(cache, (3));
292     }
293 
294     @Test
295     public void testNewInstanceForEveryGet()
296     {
297         Cache<String, Long> cache1 = makeUnexpiringCache();
298         Cache<String, Long> cache2 = makeUnexpiringCache();
299 
300         assertNotSame("The cache manager should not return the same cache twice", cache1, cache2);
301     }
302 
303     protected static final Function<Cache<Integer, Integer>, Void> REMOVE_0 = new Function<Cache<Integer, Integer>, Void>()
304     {
305         @Nullable
306         @Override
307         public Void apply(Cache<Integer, Integer> input)
308         {
309             input.remove(0);
310             return null;
311         }
312     };
313 
314     protected static final Function<Cache<Integer, Integer>, Void> REMOVE_ALL = new Function<Cache<Integer, Integer>, Void>()
315     {
316         @Nullable
317         @Override
318         public Void apply(Cache<Integer, Integer> input)
319         {
320             input.removeAll();
321             return null;
322         }
323     };
324 
325     @Test
326     public void testRemoveConcurrentWithLoadLocal()
327     {
328         removeConcurrentWithLoad(factory, null, REMOVE_0);
329     }
330 
331     @Test
332     public void testRemoveAllConcurrentWithLoadLocal()
333     {
334         removeConcurrentWithLoad(factory, null, REMOVE_ALL);
335     }
336 
337     protected void removeConcurrentWithLoad(final CacheFactory factory1, @Nullable final CacheFactory factory2,
338             final Function<Cache<Integer,Integer>,Void> removeFn)
339     {
340         final Barrier doUpdate = new Barrier();
341         final CountDownLatch removeCalled = new CountDownLatch(1);
342         final Barrier afterGet = new Barrier();
343         final AtomicInteger dbValue = new AtomicInteger(1);
344 
345         final CacheLoader<Integer,Integer> loader = new CacheLoader<Integer, Integer>()
346         {
347             @Nonnull
348             @Override
349             public Integer load(@Nonnull Integer key)
350             {
351                 final int value = dbValue.get();
352                 doUpdate.trySignal();
353                 try
354                 {
355                     // Whether or not remove gets blocked by the load operation is implementation-dependent.
356                     removeCalled.await(250L, TimeUnit.MILLISECONDS);
357                 }
358                 catch (InterruptedException ie)
359                 {
360                     throw new AssertionError(ie);
361                 }
362                 afterGet.trySignal();
363                 return value;
364             }
365         };
366 
367         final Cache<Integer,Integer> cache1 = factory1.getCache("removeConcurrentWithLoad", loader,
368                 settingsBuilder().build());
369 
370         final Cache<Integer,Integer> cache2 = (factory2 != null)
371                 ? factory2.getCache("removeConcurrentWithLoad", loader, settingsBuilder().build())
372                 : cache1;
373 
374         // Make sure everything is live.  Hazelcast takes >5s to initialize sometimes.
375         cache1.removeAll();
376         cache2.removeAll();
377 
378         final TestThread blockingGet = new TestThread("blockingGet")
379         {
380             @Override
381             protected void go() throws Exception
382             {
383                 assertThat(cache1.get(0), isOneOf(1, 2));  // Either the stale or updated value is acceptable
384                 removeCalled.await();
385                 assertThat(cache1.get(0), is(2));
386             }
387         };
388 
389         final TestThread modifyRemoveGet = new TestThread("modifyRemoveGet")
390         {
391             @Override
392             protected void go() throws Exception
393             {
394                 doUpdate.await();
395                 dbValue.set(2);
396                 removeFn.apply(cache2);
397                 removeCalled.countDown();
398                 assertThat(cache2.get(0), is(2));
399             }
400         };
401 
402         runTest(blockingGet, modifyRemoveGet);
403     }
404 }