View Javadoc
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.IOException;
26  import java.util.Random;
27  import java.util.concurrent.CountDownLatch;
28  import java.util.concurrent.atomic.AtomicBoolean;
29  
30  import org.apache.directory.mavibot.btree.exception.KeyNotFoundException;
31  import org.apache.directory.mavibot.btree.serializer.LongSerializer;
32  import org.apache.directory.mavibot.btree.serializer.StringSerializer;
33  import org.junit.AfterClass;
34  import org.junit.BeforeClass;
35  import org.junit.Test;
36  import org.junit.Ignore;
37  
38  
39  /**
40   * A class to test multi-threaded operations on the btree
41   *
42   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
43   */
44  public class MultiThreadedInMemoryBtreeTest
45  {
46      /** The btree we use */
47      private static BTree<Long, String> btree;
48  
49  
50      /**
51       * Create the btree once
52       * @throws IOException If the creation failed
53       */
54      @BeforeClass
55      public static void setup() throws IOException
56      {
57          btree = BTreeFactory.createInMemoryBTree( "test", LongSerializer.INSTANCE, StringSerializer.INSTANCE );
58      }
59  
60  
61      /**
62       * Close the btree
63       */
64      @AfterClass
65      public static void shutdown() throws IOException
66      {
67          btree.close();
68      }
69  
70  
71      /**
72       * Create a btree with 50 000 elements in it
73       * @throws IOException If the creation failed
74       */
75      private void create50KBTree() throws IOException
76      {
77          Random random = new Random( System.nanoTime() );
78  
79          int nbElems = 50000;
80  
81          // Create a BTree with 50 000 entries
82          btree.setPageSize( 32 );
83  
84          for ( int i = 0; i < nbElems; i++ )
85          {
86              Long key = ( long ) random.nextLong();
87              String value = Long.toString( key );
88  
89              try
90              {
91                  btree.insert( key, value );
92  
93                  if ( i % 10000 == 0 )
94                  {
95                      System.out.println( "Written " + i + " elements" );
96                  }
97              }
98              catch ( Exception e )
99              {
100                 e.printStackTrace();
101                 System.out.println( btree );
102                 System.out.println( "Error while adding " + value );
103                 return;
104             }
105         }
106     }
107 
108 
109     /**
110      * Browse the btree in its current revision, reading all of its elements
111      * @return The number of read elements
112      * @throws IOException If the browse failed
113      */
114     private int testBrowse() throws IOException, KeyNotFoundException
115     {
116         TupleCursor<Long, String> cursor = btree.browse();
117 
118         int nb = 0;
119         long elem = Long.MIN_VALUE;
120 
121         while ( cursor.hasNext() )
122         {
123             Tuple<Long, String> res = cursor.next();
124 
125             if ( res.getKey() > elem )
126             {
127                 elem = res.getKey();
128                 nb++;
129             }
130         }
131 
132         cursor.close();
133 
134         return nb;
135     }
136 
137 
138     /**
139      * Check that we can read the btree while it is being modified. We will start
140      * 100 readers for one writer.
141      *
142      * @throws InterruptedException If the btree access failed.
143      */
144     @Test
145     public void testBrowseMultiThreads() throws InterruptedException
146     {
147         int nbThreads = 100;
148         final CountDownLatch latch = new CountDownLatch( nbThreads );
149 
150         Thread writer = new Thread()
151         {
152             public void run()
153             {
154                 try
155                 {
156                     create50KBTree();
157                 }
158                 catch ( Exception e )
159                 {
160                 }
161             }
162         };
163 
164         long t0 = System.currentTimeMillis();
165 
166         // Start the writer
167         writer.start();
168 
169         for ( int i = 0; i < nbThreads; i++ )
170         {
171             Thread test = new Thread()
172             {
173                 public void run()
174                 {
175                     try
176                     {
177                         int res = 0;
178                         int previous = -1;
179 
180                         while ( previous < res )
181                         {
182                             previous = res;
183                             res = testBrowse();
184                             Thread.sleep( 500 );
185                         }
186 
187                         latch.countDown();
188                     }
189                     catch ( Exception e )
190                     {
191                     }
192                 }
193             };
194 
195             // Start each reader
196             test.start();
197         }
198 
199         // Wait for all the readers to be done
200         latch.await();
201 
202         long t1 = System.currentTimeMillis();
203 
204         System.out.println( " Time to create 50K entries and to have " + nbThreads + " threads reading them : "
205             + ( ( t1 - t0 ) / 1000 ) + " seconds" );
206     }
207 
208 
209     /**
210      * Test that we can use many threads inserting data in a BTree
211      * @throws InterruptedException
212      */
213     @Test
214     public void testInsertMultiThreads() throws InterruptedException, IOException
215     {
216         int nbThreads = 100;
217         final CountDownLatch latch = new CountDownLatch( nbThreads );
218         final AtomicBoolean error = new AtomicBoolean(false);
219 
220         //Thread.sleep( 60000L );
221 
222         long t0 = System.currentTimeMillis();
223 
224         class MyThread extends Thread
225         {
226             private int prefix = 0;
227 
228             public void run()
229             {
230                 try
231                 {
232                     // Inject 1000 elements
233                     for ( int j = 0; j < 1000; j++ )
234                     {
235                         long value = prefix * 1000 + j;
236                         String valStr = Long.toString( value );
237                         //System.out.println( "---------------------------Inserting " + valStr + " for Thread " + Thread.currentThread().getName() );
238                         btree.insert( value, valStr );
239 
240                         if ( j % 100 == 0 )
241                         {
242                             //System.out.println( "---------------------------Inserting " + valStr + " for Thread " + Thread.currentThread().getName() );
243 //                            long res = checkBtree( prefix, 1000, j );
244 //
245 //                            if ( res != -1L )
246 //                            {
247 //                                //retry
248 //                                System.out.println( "Failure to retrieve " + j );
249 //                                latch.countDown();
250 //                                error.set( true );
251 //                                return;
252 //                            }
253                         }
254                     }
255 
256                     latch.countDown();
257                 }
258                 catch ( Exception e )
259                 {
260                     e.printStackTrace();
261                     System.out.println( e.getMessage() );
262                 }
263             }
264 
265             public MyThread( int prefix )
266             {
267                 this.prefix = prefix;
268             }
269         }
270 
271         for ( int i = 0; i < nbThreads; i++ )
272         {
273             MyThread test = new MyThread( i );
274 
275             // Start each reader
276             test.start();
277         }
278 
279         // Wait for all the readers to be done
280         latch.await();
281 
282         if ( error.get() )
283         {
284             System.out.println( "ERROR -----------------" );
285             return;
286         }
287 
288         long t1 = System.currentTimeMillis();
289 
290         // Check that the tree contains all the values
291         assertEquals( -1L, checkBtree( 1000, nbThreads ) );
292 
293         System.out.println( " Time to create 1M entries : "
294             + ( ( t1 - t0 ) ) + " milliseconds" );
295     }
296 
297 
298     private long checkBtree( int prefix, int nbElems, int currentElem ) throws IOException
299     {
300         long i = 0L;
301 
302         try
303         {
304             for ( i = 0L; i < currentElem; i++ )
305             {
306                 long key = prefix * nbElems + i;
307                 assertEquals( Long.toString( key ), btree.get( key ) );
308             }
309 
310             return -1L;
311         }
312         catch ( KeyNotFoundException knfe )
313         {
314             System.out.println( "cannot find " + ( prefix * nbElems + i ) );
315             return i;
316         }
317     }
318 
319 
320     private long checkBtree( int nbElems, int nbThreads ) throws IOException
321     {
322         long i = 0L;
323 
324         try
325         {
326             for ( long j = 0; j < nbThreads; j++ )
327             {
328                 for ( i = 0L; i < nbElems; i++ )
329                 {
330                     long key = j * nbElems + i;
331                     assertEquals( Long.toString( key ), btree.get( key ) );
332                 }
333             }
334 
335             return -1L;
336         }
337         catch ( KeyNotFoundException knfe )
338         {
339             System.out.println( "cannot find " + i );
340             return i;
341         }
342     }
343 }