View Javadoc

1   /* CachedBdbMapTest
2    * 
3    * Created on Apr 11, 2005
4    *
5    * Copyright (C) 2005 Internet Archive.
6    * 
7    * This file is part of the Heritrix web crawler (crawler.archive.org).
8    * 
9    * Heritrix is free software; you can redistribute it and/or modify
10   * it under the terms of the GNU Lesser Public License as published by
11   * the Free Software Foundation; either version 2.1 of the License, or
12   * any later version.
13   * 
14   * Heritrix is distributed in the hope that it will be useful, 
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   * GNU Lesser Public License for more details.
18   * 
19   * You should have received a copy of the GNU Lesser Public License
20   * along with Heritrix; if not, write to the Free Software
21   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22   */
23  package org.archive.util;
24  
25  import java.io.File;
26  import java.util.HashMap;
27  import java.util.concurrent.atomic.AtomicInteger;
28  import java.util.logging.Handler;
29  import java.util.logging.Level;
30  import java.util.logging.Logger;
31  
32  import org.apache.commons.lang.math.RandomUtils;
33  
34  /***
35   * @author stack
36   * @version $Date: 2011-06-09 23:34:44 +0000 (Thu, 09 Jun 2011) $, $Revision: 7175 $
37   */
38  @SuppressWarnings("deprecation")
39  public class CachedBdbMapTest extends TmpDirTestCase {
40      File envDir; 
41      private CachedBdbMap<String,HashMap<String,String>> cache;
42      
43      @SuppressWarnings("unchecked")
44      protected void setUp() throws Exception {
45          super.setUp();
46          this.envDir = new File(getTmpDir(),"CachedBdbMapTest");
47          this.envDir.mkdirs();
48          this.cache = new CachedBdbMap(this.envDir,
49              this.getClass().getName(), String.class, HashMap.class);
50      }
51      
52      protected void tearDown() throws Exception {
53          this.cache.close();
54          FileUtils.deleteDir(this.envDir);
55          super.tearDown();
56      }
57      
58      @SuppressWarnings("unchecked")
59      public void xestReadConsistencyUnderLoad() throws Exception {
60          final CachedBdbMap<String,AtomicInteger> cbdbmap = 
61              new CachedBdbMap(
62                      this.envDir, 
63                      this.getClass().getName(), 
64                      Integer.class, 
65                      AtomicInteger.class);
66          try {
67              final AtomicInteger level = new AtomicInteger(0);
68              final int keyCount = 128 * 1024; // 128K  keys
69              final int maxLevel = 64; 
70              // initial fill
71              for(int i=0; i < keyCount; i++) {
72              	AtomicInteger prevVal = cbdbmap.putIfAbsent(""+i, new AtomicInteger(level.get()));
73                  assertNull("unexpected prior value", prevVal);
74              }
75              // backward checking that all values always at level or higher
76              new Thread() {
77                  public void run() {
78                      untilmax: while(true) {
79                          for(int j=keyCount-1; j >= 0; j--) {
80                              int targetValue = level.get(); 
81                              if(targetValue>=maxLevel) {
82                                  break untilmax;
83                              }
84                              assertTrue("stale value revseq key "+j,cbdbmap.get(j).get()>=targetValue);
85                              Thread.yield();
86                          }
87                      }
88                  }
89              }.start();
90              // random checking that all values always at level or higher
91              new Thread() {
92                  public void run() {
93                      untilmax: while(true) {
94                          int j = RandomUtils.nextInt(keyCount);
95                          int targetValue = level.get(); 
96                          if(targetValue>=maxLevel) {
97                              break untilmax;
98                          }
99                          assertTrue("stale value random key "+j,
100                                 cbdbmap.get(""+j).get()>=targetValue);
101                         Thread.yield();
102                     }
103                 }
104             }.start();
105             // increment all keys
106             for(; level.get() < maxLevel; level.incrementAndGet()) {
107                 for(int k = 0; k < keyCount; k++) {
108                     int foundValue = cbdbmap.get(""+k).getAndIncrement();
109                     assertEquals("stale value preinc key "+k, level.get(), foundValue);
110                 }
111                 if(level.get() % 10 == 0) {
112                     System.out.println("level to "+level.get());
113                 }
114                 Thread.yield(); 
115             }
116         } finally {
117             cbdbmap.close();
118         }
119         // SUCCESS
120     }
121     
122     public void testBackingDbGetsUpdated() {
123         // Enable all logging. Up the level on the handlers and then
124         // on the big map itself.
125         Handler [] handlers = Logger.getLogger("").getHandlers();
126         for (int index = 0; index < handlers.length; index++) {
127             handlers[index].setLevel(Level.FINEST);
128         }
129         Logger.getLogger(CachedBdbMap.class.getName()).
130             setLevel(Level.FINEST);
131         // Set up values.
132         final String value = "value";
133         final String key = "key";
134         final int upperbound = 3;
135         // First put in empty hashmap.
136         for (int i = 0; i < upperbound; i++) {
137         	HashMap<String,String> prevVal = this.cache.putIfAbsent(key + Integer.toString(i), new HashMap<String,String>());
138             assertNull("unexpected previous value", prevVal);
139         }
140         // Now add value to hash map.
141         for (int i = 0; i < upperbound; i++) {
142             HashMap<String,String> m = this.cache.get(key + Integer.toString(i));
143             m.put(key, value);
144         }
145         this.cache.sync();
146         for (int i = 0; i < upperbound; i++) {
147             HashMap<String,String> m = this.cache.get(key + Integer.toString(i));
148             String v = m.get(key);
149             if (v == null || !v.equals(value)) {
150                 Logger.getLogger(CachedBdbMap.class.getName()).
151                     warning("Wrong value " + i);
152             }
153         }
154     }
155     
156     /***
157      * Test that in scarce memory conditions, the memory map is 
158      * expunged of otherwise unreferenced entries as expected.
159      * 
160      * NOTE: this test may be especially fragile with regard to 
161      * GC/timing issues; relies on timely finalization, which is 
162      * never guaranteed by JVM/GC. For example, it is so sensitive
163      * to CPU speed that a Thread.sleep(1000) succeeds when my 
164      * laptop is plugged in, but fails when it is on battery!
165      * 
166      * @throws InterruptedException
167      */
168     public void testMemMapCleared() throws InterruptedException {
169         System.gc(); // minimize effects of earlier test heap use
170         assertEquals(cache.memMap.size(), 0);
171         for(int i=0; i < 10000; i++) {
172             cache.putIfAbsent(""+i, new HashMap<String,String>());
173         }
174         assertEquals(cache.memMap.size(), 10000);
175         assertEquals(cache.size(), 10000);
176         TestUtils.forceScarceMemory();
177         System.gc();
178         System.runFinalization(); 
179         Thread.sleep(2000);
180         System.gc();
181         System.runFinalization(); 
182         Thread.sleep(2000);
183         // The 'canary' trick makes this explicit expunge, or
184         // an expunge triggered by a get() or put...(), unnecessary
185         // cache.expungeStaleEntries();
186         System.out.println(cache.size()+","+cache.memMap.size());
187         assertEquals(0, cache.memMap.size());
188     }
189     
190     
191     public static void main(String [] args) {
192         junit.textui.TestRunner.run(CachedBdbMapTest.class);
193     }
194 }