verifying the number of pages created
[directory-mavibot.git] / mavibot / src / test / java / org / apache / directory / mavibot / btree / SpaceReclaimerTest.java
1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 *
19 */
20 package org.apache.directory.mavibot.btree;
21
22
23 import static org.junit.Assert.assertEquals;
24
25 import java.io.File;
26 import java.util.Map;
27 import java.util.Random;
28 import java.util.concurrent.ConcurrentHashMap;
29 import java.util.concurrent.CountDownLatch;
30
31 import org.apache.directory.mavibot.btree.serializer.IntSerializer;
32 import org.apache.directory.mavibot.btree.serializer.StringSerializer;
33 import org.junit.After;
34 import org.junit.Before;
35 import org.junit.Rule;
36 import org.junit.Test;
37 import org.junit.rules.TemporaryFolder;
38
39 /**
40 * Tests for free page reclaimer.
41 *
42 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
43 */
44 public class SpaceReclaimerTest
45 {
46 private static final String TREE_NAME = "uid-tree";
47
48 private RecordManager rm;
49
50 private PersistedBTree<Integer, String> uidTree;
51
52 @Rule
53 public TemporaryFolder tmpDir;
54
55 private File dbFile;
56
57
58 @Before
59 public void setup() throws Exception
60 {
61 tmpDir = new TemporaryFolder();
62 tmpDir.create();
63
64 dbFile = tmpDir.newFile( "spacereclaimer.db" );
65
66 System.out.println(dbFile.getAbsolutePath());
67 rm = new RecordManager( dbFile.getAbsolutePath() );
68 rm.setSpaceReclaimerThreshold( 10 );
69
70 uidTree = ( PersistedBTree<Integer, String> ) rm.addBTree( TREE_NAME, IntSerializer.INSTANCE, StringSerializer.INSTANCE, false );
71 }
72
73
74 @After
75 public void cleanup() throws Exception
76 {
77 rm.close();
78 dbFile.delete();
79 tmpDir.delete();
80 }
81
82
83 private void closeAndReopenRM() throws Exception
84 {
85 uidTree.close();
86 rm.close();
87 rm = new RecordManager( dbFile.getAbsolutePath() );
88 uidTree = ( PersistedBTree ) rm.getManagedTree( TREE_NAME );
89 }
90
91
92 @Test
93 public void testReclaimer() throws Exception
94 {
95 int total = 11;
96 System.out.println( dbFile.length() );
97 for ( int i=0; i < total; i++ )
98 {
99 uidTree.insert( i, String.valueOf( i ) );
100 }
101
102 System.out.println( "Total size before closing " + dbFile.length() );
103 System.out.println( dbFile.length() );
104 closeAndReopenRM();
105 System.out.println( "Total size AFTER closing " + dbFile.length() );
106
107 int count = 0;
108 TupleCursor<Integer, String> cursor = uidTree.browse();
109 while ( cursor.hasNext() )
110 {
111 Tuple<Integer, String> t = cursor.next();
112 assertEquals( t.key, Integer.valueOf( count ) );
113 count++;
114 }
115
116 assertEquals( count, total );
117 }
118
119
120 /**
121 * with the reclaimer threshold 10 and total entries of 1120
122 * there was a condition that resulted in OOM while reopening the RM
123 *
124 * This issue was fixed after SpaceReclaimer was updated to run in
125 * a transaction.
126 *
127 * This test is present to verify the fix
128 *
129 * @throws Exception
130 */
131 @Test
132 public void testReclaimerWithMagicNum() throws Exception
133 {
134 rm.setSpaceReclaimerThreshold( 10 );
135
136 int total = 1120;
137 for ( int i=0; i < total; i++ )
138 {
139 uidTree.insert( i, String.valueOf( i ) );
140 }
141
142 closeAndReopenRM();
143
144 int count = 0;
145 TupleCursor<Integer, String> cursor = uidTree.browse();
146 while ( cursor.hasNext() )
147 {
148 Tuple<Integer, String> t = cursor.next();
149 assertEquals( t.key, Integer.valueOf( count ) );
150 count++;
151 }
152
153 assertEquals( count, total );
154 }
155
156
157 /**
158 * Test reclaimer functionality while multiple threads writing to the same BTree
159 *
160 * @throws Exception
161 */
162 @Test
163 public void testReclaimerWithMultiThreads() throws Exception
164 {
165 final int numEntriesPerThread = 11;
166 final int numThreads = 5;
167
168 final int total = numThreads * numEntriesPerThread;
169
170 final Map<Integer, Integer> keyMap = new ConcurrentHashMap<Integer, Integer>();
171
172 final Random rnd = new Random();
173
174 final CountDownLatch latch = new CountDownLatch( numThreads );
175
176 Runnable r = new Runnable()
177 {
178 @Override
179 public void run()
180 {
181 for ( int i=0; i < numEntriesPerThread; i++ )
182 {
183 try
184 {
185 int key = rnd.nextInt( total );
186 while( true )
187 {
188 if( !keyMap.containsKey( key ) )
189 {
190 keyMap.put( key, key );
191 break;
192 }
193
194 //System.out.println( "duplicate " + key );
195 key = rnd.nextInt( total );
196 }
197
198 uidTree.insert( key, String.valueOf( key ) );
199 }
200 catch( Exception e )
201 {
202 throw new RuntimeException(e);
203 }
204 }
205
206 latch.countDown();
207 }
208 };
209
210 for ( int i=0; i<numThreads; i++ )
211 {
212 Thread t = new Thread( r );
213 t.start();
214 }
215
216 latch.await();
217
218 System.out.println( "Total size before closing " + dbFile.length() );
219 closeAndReopenRM();
220 System.out.println( "Total size AFTER closing " + dbFile.length() );
221
222 int count = 0;
223 TupleCursor<Integer, String> cursor = uidTree.browse();
224 while ( cursor.hasNext() )
225 {
226 Tuple<Integer, String> t = cursor.next();
227 assertEquals( t.key, Integer.valueOf( count ) );
228 count++;
229 }
230
231 cursor.close();
232
233 assertEquals( count, total );
234 }
235
236 @Test
237 public void testInspectTreeState() throws Exception
238 {
239 File file = File.createTempFile( "freepagedump", ".db" );
240 RecordManager manager = new RecordManager( file.getAbsolutePath() );
241 manager._disableReclaimer( true );
242
243 PersistedBTreeConfiguration config = new PersistedBTreeConfiguration();
244
245 config.setName( "dump-tree" );
246 config.setKeySerializer( IntSerializer.INSTANCE );
247 config.setValueSerializer( StringSerializer.INSTANCE );
248 config.setAllowDuplicates( false );
249 config.setPageSize( 4 );
250
251 BTree btree = new PersistedBTree( config );
252 manager.manage( btree );
253
254 // insert 5 so that we get 1 root and 2 child nodes
255 for( int i=0; i<5; i++ )
256 {
257 btree.insert( i, String.valueOf( i ) );
258 }
259
260 System.out.println( "Total number of pages created " + manager.nbCreatedPages );
261 System.out.println( "Total number of pages reused " + manager.nbReusedPages );
262 System.out.println( "Total number of pages freed " + manager.nbFreedPages );
263 System.out.println( "Total file size (bytes) " + file.length() );
264
265 long totalPages = file.length() / RecordManager.DEFAULT_PAGE_SIZE;
266
267 // in RM the header page gets skipped before incrementing nbCreatedPages
268 assertEquals( manager.nbCreatedPages.get()+1, totalPages );
269
270 System.out.println(btree.getRootPage());
271 System.out.println( file.getAbsolutePath() );
272 manager.close();
273 }
274 }