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