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;
18
19 import junit.framework.Assert;
20 import net.sf.ehcache.store.LruMemoryStoreTest;
21 import net.sf.ehcache.store.MemoryStore;
22 import net.sf.ehcache.store.MemoryStoreEvictionPolicy;
23 import net.sf.ehcache.store.Store;
24 import org.junit.After;
25 import org.junit.Before;
26 import org.junit.Ignore;
27 import org.junit.Test;
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
30
31 import java.io.IOException;
32 import java.util.ArrayList;
33 import java.util.Date;
34 import java.util.List;
35 import java.util.Random;
36
37 import static junit.framework.Assert.assertTrue;
38 import static org.junit.Assert.*;
39
40 /***
41 * Other than policy differences, the Store implementations should work identically
42 *
43 * @author Greg Luck
44 * @version $Id: MemoryStoreTester.html 13146 2011-08-01 17:12:39Z oletizi $
45 */
46 @Ignore
47 public class MemoryStoreTester extends AbstractCacheTest {
48
49 private static final Logger LOG = LoggerFactory.getLogger(MemoryStoreTester.class.getName());
50
51 /***
52 * The memory store that tests will be performed on
53 */
54 protected Store store;
55
56
57 /***
58 * The cache under test
59 */
60 protected Cache cache;
61
62
63 /***
64 * For automatic suite generators
65 */
66 @Test
67 public void testNoop() {
68
69 }
70
71 /***
72 * setup test
73 */
74 @Override
75 @Before
76 public void setUp() throws Exception {
77 manager = CacheManager.getInstance();
78 }
79
80 /***
81 * teardown
82 */
83 @Override
84 @After
85 public void tearDown() throws Exception {
86 try {
87 if (cache != null) {
88 cache.removeAll();
89 cache = null;
90 }
91 if (manager != null) {
92 manager.shutdown();
93 manager = null;
94 }
95 } catch (OutOfMemoryError e) {
96
97 LOG.info(e.getMessage());
98 } catch (Throwable t) {
99
100 LOG.info(t.getMessage());
101 }
102 }
103
104 /***
105 * Creates a cache with the given policy and adds it to the manager.
106 *
107 * @param evictionPolicy
108 * @throws CacheException
109 */
110 protected void createMemoryOnlyStore(MemoryStoreEvictionPolicy evictionPolicy) throws CacheException {
111 manager.removeCache("testMemoryOnly");
112 cache = new Cache("testMemoryOnly", 12000, evictionPolicy, false, System.getProperty("java.io.tmpdir"),
113 false, 60, 30, false, 60, null);
114 manager.addCache(cache);
115 store = cache.getStore();
116 }
117
118 /***
119 * Creates a cache with the given policy with a MemoryStore only and adds it to the manager.
120 *
121 * @param evictionPolicy
122 * @throws CacheException
123 */
124 protected void createMemoryOnlyStore(MemoryStoreEvictionPolicy evictionPolicy, int memoryStoreSize) throws CacheException {
125 manager.removeCache("test");
126 cache = new Cache("test", memoryStoreSize, evictionPolicy, false, null, false, 60, 30, false, 60, null);
127 manager.addCache(cache);
128 store = cache.getStore();
129 }
130
131 /***
132 * Creates a store from the given configuration and cache within it.
133 *
134 * @param filePath
135 * @param cacheName
136 * @throws CacheException
137 */
138 protected void createMemoryStore(String filePath, String cacheName) throws CacheException {
139 manager.shutdown();
140 manager = CacheManager.create(filePath);
141 cache = manager.getCache(cacheName);
142 store = cache.getStore();
143 }
144
145
146 /***
147 * Test elements can be put in the store
148 */
149 protected void putTest() throws IOException {
150 Element element;
151
152 assertEquals(0, store.getSize());
153
154 element = new Element("key1", "value1");
155 store.put(element);
156 assertEquals(1, store.getSize());
157 assertEquals("value1", store.get("key1").getObjectValue());
158
159 element = new Element("key2", "value2");
160 store.put(element);
161 assertEquals(2, store.getSize());
162 assertEquals("value2", store.get("key2").getObjectValue());
163
164 for (int i = 0; i < 1999; i++) {
165 store.put(new Element("" + i, new Date()));
166 }
167
168 assertEquals(4, store.getSize());
169 assertEquals(2001, cache.getSize());
170 assertEquals(3998, cache.getDiskStoreSize());
171
172 /***
173 * Non serializable test class
174 */
175 class NonSerializable {
176
177 }
178
179 store.put(new Element(new NonSerializable(), new NonSerializable()));
180
181 assertEquals(4, store.getSize());
182 assertEquals(2002, cache.getSize());
183 assertEquals(1999, cache.getDiskStoreSize());
184
185
186
187 for (int i = 0; i < 2000; i++) {
188 store.get("" + i);
189 }
190 }
191
192 /***
193 * Test elements can be removed from the store
194 */
195 protected void removeTest() throws IOException {
196 Element element;
197
198 element = new Element("key1", "value1");
199 store.put(element);
200 assertEquals(1, store.getSize());
201
202 store.remove("key1");
203 assertEquals(0, store.getSize());
204
205 store.put(new Element("key2", "value2"));
206 store.put(new Element("key3", "value3"));
207 assertEquals(2, store.getSize());
208
209 assertNotNull(store.remove("key2"));
210 assertEquals(1, store.getSize());
211
212
213 assertNull(store.remove("key4"));
214 assertEquals(1, store.getSize());
215
216
217 assertNull(store.remove(null));
218
219 }
220
221
222 /***
223 * Check no NPE on put
224 */
225 @Test
226 public void testNullPut() throws IOException {
227 store.put(null);
228 }
229
230 /***
231 * Check no NPE on get
232 */
233 @Test
234 public void testNullGet() throws IOException {
235 assertNull(store.get(null));
236 }
237
238 /***
239 * Check no NPE on remove
240 */
241 @Test
242 public void testNullRemove() throws IOException {
243 assertNull(store.remove(null));
244 }
245
246 /***
247 * Tests looking up an entry that does not exist.
248 */
249 @Test
250 public void testGetUnknown() throws Exception {
251 final Element element = store.get("key");
252 assertNull(element);
253 }
254
255 /***
256 * Tests adding an entry.
257 */
258 @Test
259 public void testPut() throws Exception {
260 final String value = "value";
261 final String key = "key";
262
263
264 assertEquals(0, store.getSize());
265 Element element = store.get(key);
266 assertNull(element);
267
268
269 element = new Element(key, value);
270 store.put(element);
271
272
273 assertEquals(1, store.getSize());
274 element = store.get(key);
275 assertNotNull(element);
276 assertEquals(value, element.getObjectValue());
277 }
278
279 /***
280 * Tests removing an entry.
281 */
282 @Test
283 public void testRemove() throws Exception {
284 final String value = "value";
285 final String key = "key";
286
287
288
289 Element element = new Element(key, value);
290 store.put(element);
291
292
293 assertEquals(1, store.getSize());
294 element = store.get(key);
295 assertNotNull(element);
296
297
298 store.remove(key);
299
300
301 assertEquals(0, store.getSize());
302 element = store.get(key);
303 assertNull(element);
304 }
305
306 /***
307 * Tests removing all the entries.
308 */
309 @Test
310 public void testRemoveAll() throws Exception {
311 final String value = "value";
312 final String key = "key";
313
314
315 Element element = new Element(key, value);
316 store.put(element);
317
318
319 element = store.get(key);
320 assertNotNull(element);
321
322
323 store.removeAll();
324
325
326 assertEquals(0, store.getSize());
327 element = store.get(key);
328 assertNull(element);
329 }
330
331 /***
332 * Multi-thread read-only test.
333 */
334 @Test
335 public void testReadOnlyThreads() throws Exception {
336
337
338 store.put(new Element("key0", "value"));
339 store.put(new Element("key1", "value"));
340
341
342 final List executables = new ArrayList();
343 for (int i = 0; i < 10; i++) {
344 final String key = "key" + (i % 2);
345 final MemoryStoreTester.Executable executable = new LruMemoryStoreTest.Executable() {
346 public void execute() throws Exception {
347 final Element element = store.get(key);
348 assertNotNull(element);
349 assertEquals("value", element.getObjectValue());
350 }
351 };
352 executables.add(executable);
353 }
354 runThreads(executables);
355 }
356
357 /***
358 * Multi-thread read-write test.
359 */
360 @Test
361 public void testReadWriteThreads() throws Exception {
362
363 final String value = "value";
364 final String key = "key";
365
366
367 final Element element = new Element(key, value);
368 store.put(element);
369
370
371 final List executables = new ArrayList();
372 for (int i = 0; i < 5; i++) {
373 final MemoryStoreTester.Executable executable = new MemoryStoreTester.Executable() {
374 public void execute() throws Exception {
375 final Element element = store.get("key");
376 assertNotNull(element);
377 }
378 };
379 executables.add(executable);
380 }
381 for (int i = 0; i < 5; i++) {
382 final MemoryStoreTester.Executable executable = new MemoryStoreTester.Executable() {
383 public void execute() throws Exception {
384 store.put(element);
385 }
386 };
387 executables.add(executable);
388 }
389
390 runThreads(executables);
391 }
392
393 /***
394 * Multi-thread read, put and removeAll test.
395 * This checks for memory leaks
396 * using the removeAll which was the known cause of memory leaks with MemoryStore in JCS
397 */
398 @Test
399 public void testMemoryLeak() throws Exception {
400 long differenceMemoryCache = thrashCache();
401 LOG.info("Difference is : " + differenceMemoryCache);
402
403 assertTrue("Memory difference was expected to be less than 500000, but was " + differenceMemoryCache, differenceMemoryCache < 500000);
404 }
405
406
407 /***
408 * This method tries to get the store too leak.
409 */
410 protected long thrashCache() throws Exception {
411
412
413 long startingSize = measureMemoryUse();
414 LOG.info("Starting memory used is: " + startingSize);
415
416 final String value = "value";
417 final String key = "key";
418
419
420 final Element element = new Element(key, value);
421 store.put(element);
422
423
424 final List executables = new ArrayList();
425 for (int i = 0; i < 15; i++) {
426 final LruMemoryStoreTest.Executable executable = new MemoryStoreTester.Executable() {
427 public void execute() throws Exception {
428 for (int i = 0; i < 500; i++) {
429 final String key = "key" + i;
430 store.get(key);
431 }
432 store.get("key");
433 }
434 };
435 executables.add(executable);
436 }
437
438 for (int i = 0; i < 15; i++) {
439 final MemoryStoreTester.Executable executable = new MemoryStoreTester.Executable() {
440 public void execute() throws Exception {
441
442
443 for (int i = 0; i < 500; i++) {
444
445 final String key = "key" + i;
446 byte[] value = new byte[10000];
447 Element element = new Element(key, value);
448 store.put(element);
449 }
450 }
451 };
452 executables.add(executable);
453 }
454
455 runThreads(executables);
456 store.removeAll();
457
458 long finishingSize = measureMemoryUse();
459 LOG.info("Ending memory used is: " + finishingSize);
460 return finishingSize - startingSize;
461 }
462
463
464 /***
465 * Multi-thread read-write test.
466 */
467 @Test
468 public void testReadWriteThreadsSurya() throws Exception {
469
470 long start = System.currentTimeMillis();
471 final List executables = new ArrayList();
472 final Random random = new Random();
473
474
475 for (int i = 0; i < 10; i++) {
476 final Executable executable = new Executable() {
477 public void execute() throws Exception {
478 store.get("key" + random.nextInt(10000));
479 }
480 };
481 executables.add(executable);
482 }
483
484
485 for (int i = 0; i < 5; i++) {
486 final Executable executable = new Executable() {
487 public void execute() throws Exception {
488 store.put(new Element("key" + random.nextInt(20000), "value"));
489 }
490 };
491 executables.add(executable);
492 }
493
494
495 for (int i = 0; i < 5; i++) {
496 final Executable executable = new Executable() {
497 public void execute() throws Exception {
498 store.remove("key" + random.nextInt(10000));
499 }
500 };
501 executables.add(executable);
502 }
503
504 runThreads(executables);
505 long end = System.currentTimeMillis();
506 LOG.info("Total time for the test: " + (end + start) + " ms");
507 }
508
509
510 /***
511 * Test behaviour of memory store using 1 million records.
512 * This is expected to run out of memory on a 64MB machine. Where it runs out
513 * is asserted so that design changes do not start using more memory per element.
514 * <p/>
515 * This test will fail (ie not get an out of memory error) on VMs configured to be server which do not have a fixed upper memory limit.
516 * <p/>
517 * Takes too long to run therefore switch off
518 * <p/>
519 * These memory size asserts were 100,000 and 60,000. The ApacheLRU map does not get quite as high numbers.
520 * This test varies according to architecture. 64 bit architectures
521 */
522 @Test
523 public void testMemoryStoreOutOfMemoryLimit() throws Exception {
524 LOG.info("Starting out of memory limit test");
525
526 cache = manager.getCache("memoryLimitTest");
527 if (cache == null) {
528 cache = new Cache("memoryLimitTest", 1000000, false, false, 500, 500);
529 manager.addCache(cache);
530 }
531 int i = 0;
532 try {
533 for (; i < 1000000; i++) {
534 cache.put(new Element("" +
535 i, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
536 + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
537 + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
538 + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
539 + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
540 + "AAAAA " + i));
541 }
542 LOG.info("About to fail out of memory limit test");
543 fail();
544 } catch (OutOfMemoryError e) {
545 cache.removeAll();
546 Thread.sleep(1000);
547 System.gc();
548 Thread.sleep(2000);
549 System.gc();
550
551 try {
552 LOG.info("Ran out of memory putting " + i + "th element");
553 assertTrue(i > 65000);
554 } catch (OutOfMemoryError e1) {
555
556 }
557
558 }
559 Thread.sleep(1000);
560 System.gc();
561 Thread.sleep(1000);
562 }
563
564 @Test
565 public void testShrinkingAndGrowingMemoryStore() {
566 cache = new Cache("testShrinkingAndGrowingMemoryStore", 50, false, false, 120, 120);
567 manager.addCache(cache);
568 store = cache.getStore();
569
570 if (!(store instanceof MemoryStore)) {
571 LOG.info("Skipping Growing/Shrinking Memory Store Test - Store is not a subclass of MemoryStore!");
572 return;
573 }
574
575 int i = 0;
576 for (; ;) {
577 int size = store.getSize();
578 store.put(new Element(Integer.valueOf(i++), new byte[100]));
579 if (store.getSize() <= size) break;
580 }
581
582 final int initialSize = store.getSize();
583 final int shrinkSize = initialSize / 2;
584 ((MemoryStore) store).memoryCapacityChanged(initialSize, shrinkSize);
585
586 for (; ;) {
587 int size = store.getSize();
588 store.put(new Element(Integer.valueOf(i++), new byte[100]));
589 if (store.getSize() >= size) break;
590 }
591
592 {
593 int size = store.getSize();
594 assertTrue(size < (shrinkSize * 1.1));
595 assertTrue(size > (shrinkSize * 0.9));
596 }
597
598 final int growSize = initialSize * 2;
599 ((MemoryStore) store).memoryCapacityChanged(shrinkSize, growSize);
600
601 for (; ;) {
602 int size = store.getSize();
603 store.put(new Element(Integer.valueOf(i++), new byte[100]));
604 if (store.getSize() <= size) break;
605 }
606
607 {
608 int size = store.getSize();
609 assertTrue(size < (growSize * 1.1));
610 assertTrue(size > (growSize * 0.9));
611 }
612 }
613
614 @Test
615 public void testElementPinning() throws Exception {
616 createMemoryOnlyStore(MemoryStoreEvictionPolicy.LRU, 20);
617
618 for (int i = 0; i < 200; i++) {
619 Element element = new Element("Ku-" + i, "@" + i);
620 store.put(element);
621 }
622
623 Assert.assertEquals(20, store.getSize());
624
625 for (int i = 0; i < 200; i++) {
626 Element element = new Element("Kp-" + i, "#" + i);
627 element.setTimeToIdle(1);
628 element.setTimeToLive(1);
629 element.setPinned(true);
630 store.put(element);
631 }
632
633 for (int i = 0; i < 200; i++) {
634 assertTrue("missing key Kp-" + i, store.containsKey("Kp-" + i));
635 }
636
637
638 Thread.sleep(1100);
639
640 for (int i = 1000; i < 1200; i++) {
641 Element element = new Element("Ku-" + i, "#" + i);
642 store.put(element);
643 }
644
645 Assert.assertEquals(20, store.getSize());
646 }
647
648 }