View Javadoc

1   /***
2    *  Copyright 2003-2010 Terracotta, Inc.
3    *
4    *  Licensed under the Apache License, Version 2.0 (the "License");
5    *  you may not use this file except in compliance with the License.
6    *  You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License.
15   */
16  
17  package net.sf.ehcache.store;
18  
19  import static org.junit.Assert.assertEquals;
20  import static org.junit.Assert.assertFalse;
21  import static org.junit.Assert.assertNotNull;
22  import static org.junit.Assert.assertNull;
23  import static org.junit.Assert.assertTrue;
24  
25  import java.io.IOException;
26  import java.io.Serializable;
27  import java.lang.reflect.Field;
28  import java.lang.reflect.Method;
29  import java.util.Date;
30  import java.util.Iterator;
31  import java.util.List;
32  import java.util.Map;
33  import java.util.Random;
34  import java.util.concurrent.ConcurrentHashMap;
35  
36  import net.sf.ehcache.AbstractCacheTest;
37  import net.sf.ehcache.Cache;
38  import net.sf.ehcache.CacheManager;
39  import net.sf.ehcache.Element;
40  import net.sf.ehcache.MemoryStoreTester;
41  import net.sf.ehcache.Statistics;
42  import net.sf.ehcache.store.compound.CompoundStore;
43  import net.sf.ehcache.store.compound.ElementSubstituteFilter;
44  import net.sf.ehcache.store.compound.factories.CapacityLimitedInMemoryFactory;
45  
46  import org.junit.Before;
47  import org.junit.Test;
48  import org.slf4j.Logger;
49  import org.slf4j.LoggerFactory;
50  
51  /***
52   * Test class for LfuMemoryStore
53   * <p/>
54   *
55   * @author <a href="ssuravarapu@users.sourceforge.net">Surya Suravarapu</a>
56   * @version $Id: LfuMemoryStoreTest.html 13146 2011-08-01 17:12:39Z oletizi $
57   */
58  public class LfuMemoryStoreTest extends MemoryStoreTester {
59  
60      private static final Logger LOG = LoggerFactory.getLogger(LfuMemoryStoreTest.class.getName());
61  
62      private static final Field PRIMARY_FACTORY;
63      private static final Method GET_EVICTION_TARGET;
64  
65      static {
66          try {
67              PRIMARY_FACTORY = CompoundStore.class.getDeclaredField("primary");
68              PRIMARY_FACTORY.setAccessible(true);
69              GET_EVICTION_TARGET = CapacityLimitedInMemoryFactory.class.getDeclaredMethod("getEvictionTarget", Object.class, Integer.TYPE);
70              GET_EVICTION_TARGET.setAccessible(true);
71          } catch (SecurityException e) {
72              throw new RuntimeException(e);
73          } catch (NoSuchFieldException e) {
74              throw new RuntimeException(e);
75          } catch (NoSuchMethodException e) {
76              throw new RuntimeException(e);
77          }
78      }
79  
80      /***
81       * setup test
82       */
83      @Override
84      @Before
85      public void setUp() throws Exception {
86          super.setUp();
87          createMemoryOnlyStore(MemoryStoreEvictionPolicy.LFU);
88      }
89  
90  
91      /***
92       * Check no NPE on get
93       */
94      @Override
95      @Test
96      public void testNullGet() throws IOException {
97          assertNull(store.get(null));
98      }
99  
100     /***
101      * Check no NPE on remove
102      */
103     @Override
104     @Test
105     public void testNullRemove() throws IOException {
106         assertNull(store.remove(null));
107     }
108 
109     /***
110      * Tests the put by reading the config file
111      */
112     @Test
113     public void testPutFromConfigZeroMemoryStore() throws Exception {
114         createMemoryStore(AbstractCacheTest.TEST_CONFIG_DIR + "ehcache-policy-test.xml", "sampleLFUCache2");
115         Element element = new Element("1", "value");
116         store.put(element);
117         assertNotNull(store.get("1"));
118     }
119 
120     /***
121      * Tests the remove() method by using the parameters specified in the config file
122      */
123     @Test
124     public void testRemoveFromConfig() throws Exception {
125         createMemoryStore(AbstractCacheTest.TEST_CONFIG_DIR + "ehcache-policy-test.xml", "sampleLFUCache1");
126         removeTest();
127     }
128 
129 
130     /***
131      * Tests the LFU policy
132      */
133     @Test
134     public void testLfuPolicy() throws Exception {
135         createMemoryOnlyStore(MemoryStoreEvictionPolicy.LFU, 4);
136         lfuPolicyTest();
137     }
138 
139     /***
140      * Tests the LFU policy by using the parameters specified in the config file
141      */
142     @Test
143     public void testLfuPolicyFromConfig() throws Exception {
144         createMemoryStore(AbstractCacheTest.TEST_CONFIG_DIR + "ehcache-policy-test.xml", "sampleLFUCache1");
145         lfuPolicyTest();
146     }
147 
148 
149     private void lfuPolicyTest() throws IOException, InterruptedException {
150         //Make sure that the store is empty to start with
151         assertEquals(0, cache.getSize());
152 
153         // Populate the store till the max limit
154         Element element = new Element("key1", "value1");
155         cache.put(element);
156         assertEquals(1, store.getInMemorySize());
157 
158         element = new Element("key2", "value2");
159         cache.put(element);
160         assertEquals(2, store.getInMemorySize());
161 
162         element = new Element("key3", "value3");
163         cache.put(element);
164         assertEquals(3, store.getInMemorySize());
165 
166         element = new Element("key4", "value4");
167         cache.put(element);
168         assertEquals(4, store.getInMemorySize());
169 
170         //Now access the elements to boost the hit count
171         cache.get("key1");
172         cache.get("key1");
173         cache.get("key3");
174         cache.get("key3");
175         cache.get("key3");
176         cache.get("key4");
177 
178         //Create a new element and put in the store so as to force the policy
179         element = new Element("key5", "value5");
180         cache.put(element);
181 
182         Thread.sleep(200);
183 
184         assertEquals(4, store.getInMemorySize());
185         //The element with key "key2" is the LFU element so should be removed
186         // directly access the memory store here since the LFU evicted elements have been flushed to the disk store
187         assertFalse(((CompoundStore) store).unretrievedGet("key2") instanceof Element);
188 
189         // Make some more accesses
190         cache.get("key5");
191         cache.get("key5");
192 
193         // Insert another element to force the policy
194         element = new Element("key6", "value6");
195         cache.put(element);
196 
197         Thread.sleep(200);
198 
199         assertEquals(4, store.getInMemorySize());
200         assertFalse(((CompoundStore) store).unretrievedGet("key2") instanceof Element);
201     }
202 
203 
204     /***
205      * Multi-thread read, put and removeAll test.
206      * This checks for memory leaks
207      * using the removeAll which was the known cause of memory leaks with LruMemoryStore in JCS
208      * new sampling LFU has no leaks
209      */
210     @Override
211     @Test
212     public void testMemoryLeak() throws Exception {
213         super.testMemoryLeak();
214     }
215 
216     /***
217      * Tests how random the java.util.Map iteration is by measuring the differences in iterate order.
218      * <p/>
219      * If iterate was ordered in either insert or reverse insert order the mean difference would be 1.
220      * Using Random gives a mean difference of 343.
221      * The observed value is 75, always 75 for a key set of 500 because it always iterates in the same order,
222      * just not an obvious order.
223      * <p/>
224      * Conclusion: Unable to use the iterator as a pseudorandom selector.
225      */
226     @Test
227     public void testRandomnessOfIterator() {
228         int mean = 0;
229         int absoluteDifferences = 0;
230         int lastReading = 0;
231         Map map = new ConcurrentHashMap();
232         for (int i = 1; i <= 500; i++) {
233             mean += i;
234             map.put("" + i, " ");
235         }
236         mean = mean / 500;
237         for (Iterator iterator = map.keySet().iterator(); iterator.hasNext();) {
238             String string = (String) iterator.next();
239             int thisReading = Integer.parseInt(string);
240             LOG.info("reading: " + thisReading);
241             absoluteDifferences += Math.abs(lastReading - thisReading);
242             lastReading = thisReading;
243         }
244         LOG.debug("Mean difference through iteration: " + absoluteDifferences / 500);
245 
246         //Random selection without replacement
247         Random random = new Random();
248         while (map.size() != 0) {
249             int thisReading = random.nextInt(501);
250             Object o = map.remove("" + thisReading);
251             if (o == null) {
252                 continue;
253             }
254             absoluteDifferences += Math.abs(lastReading - thisReading);
255             lastReading = thisReading;
256         }
257         LOG.info("Mean difference with random selection without replacement : " + absoluteDifferences / 500);
258         LOG.info("Mean of range 1 - 500 : " + mean);
259 
260     }
261 
262 
263     private static final ElementSubstituteFilter<Element> IDENTITY_FILTER = new ElementSubstituteFilter<Element>() {
264         public boolean allows(Object object) {
265             return object instanceof Element;
266         }
267     };
268 
269     /***
270      * Check nothing breaks and that we get the right number of samples
271      *
272      * @throws IOException
273      */
274     @Test
275     public void testSampling() throws IOException {
276         createMemoryOnlyStore(MemoryStoreEvictionPolicy.LFU, 1000);
277         List<Element> elements = null;
278         for (int i = 0; i < 10; i++) {
279             store.put(new Element("" + i, new Date()));
280             elements = ((CompoundStore) store).getRandomSample(IDENTITY_FILTER, i + 1, new Object());
281         }
282 
283         for (int i = 10; i < 2000; i++) {
284             store.put(new Element("" + i, new Date()));
285             elements = ((CompoundStore) store).getRandomSample(IDENTITY_FILTER, 10, new Object());
286             assertTrue(elements.size() >= 10);
287         }
288     }
289 
290 
291     /***
292      * Can we deal with NonSerializable objects?
293      */
294     @Test
295     public void testNonSerializable() {
296         /***
297          * Non-serializable test class
298          */
299         class NonSerializable {
300             //
301         }
302         NonSerializable key = new NonSerializable();
303         store.put(new Element(key, new NonSerializable()));
304         store.get(key);
305     }
306 
307 
308     /***
309      * Test which reproduced an issue with flushing of an LFU store to disk on shutdown
310      */
311     @Test
312     public void testPersistLFUMemoryStore() {
313         manager.shutdown();
314         CacheManager cacheManager = new CacheManager(AbstractCacheTest.TEST_CONFIG_DIR + "ehcache-policy-test.xml");
315         Cache cache = cacheManager.getCache("test-cache");
316 
317         getTestBean(cache, "test1");
318         getTestBean(cache, "test2");
319         getTestBean(cache, "test1");
320         getTestBean(cache, "test1");
321         getTestBean(cache, "test3");
322         getTestBean(cache, "test3");
323         getTestBean(cache, "test4");
324         getTestBean(cache, "test2");
325 
326         Statistics stats = cache.getStatistics();
327         LOG.info(stats.toString());
328 
329         cacheManager.shutdown();
330     }
331 
332     private TestBean getTestBean(Cache cache, String key) {
333         Element element = cache.get(key);
334         if (element == null) {
335             element = new Element(key, new TestBean(key + "_value"));
336             cache.put(element);
337         }
338         return (TestBean) element.getValue();
339     }
340 
341 
342     /***
343      * A simple persistent JavaBean
344      *
345      * @author <a href="mailto:gluck@gregluck.com">Greg Luck</a>
346      * @version $Id: LfuMemoryStoreTest.html 13146 2011-08-01 17:12:39Z oletizi $
347      */
348     private final class TestBean implements Serializable {
349 
350         private String string;
351 
352         private TestBean() {
353             //noop
354         }
355 
356         /***
357          * Constructor
358          *
359          * @param string
360          */
361         private TestBean(String string) {
362             this.string = string;
363         }
364     }
365 
366 
367 }
368 
369 
370