You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by el...@apache.org on 2014/05/17 15:31:25 UTC

svn commit: r1595477 [4/8] - in /directory/mavibot/trunk: ./ mavibot/img/ mavibot/src/main/java/org/apache/directory/mavibot/btree/ mavibot/src/main/java/org/apache/directory/mavibot/btree/comparator/ mavibot/src/main/java/org/apache/directory/mavibot/...

Modified: directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/PageIO.java
URL: http://svn.apache.org/viewvc/directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/PageIO.java?rev=1595477&r1=1595476&r2=1595477&view=diff
==============================================================================
--- directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/PageIO.java (original)
+++ directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/PageIO.java Sat May 17 13:31:23 2014
@@ -34,25 +34,25 @@ import org.apache.directory.mavibot.btre
  * Here is the logical structure of a PageIO :
  * <pre>
  * For a first page :
- * 
+ *
  * +----------+------+----------------------+
  * | nextPage | size | XXXXXXXXXXXXXXXXXXXX |
  * +----------+------+----------------------+
- * 
+ *
  * for any page but the first :
- * 
+ *
  * +----------+-----------------------------+
  * | nextPage | XXXXXXXXXXXXXXXXXXXXXXXXXXX |
  * +----------+-----------------------------+
- * 
+ *
  * for the last page :
  * +----------+-----------------------------+
  * |    -1    | XXXXXXXXXXXXXXXXXXXXXXXXXXX |
  * +----------+-----------------------------+
- * 
+ *
  * In any case, the page length is always PageSize.
  * </pre>
- *  
+ *
  * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
  */
 /* No qualifier*/class PageIO
@@ -70,7 +70,7 @@ import org.apache.directory.mavibot.btre
     private long offset;
 
 
-    /** 
+    /**
      * A default constructor for a PageIO
      */
     /* no qualifier */PageIO()
@@ -81,7 +81,7 @@ import org.apache.directory.mavibot.btre
     }
 
 
-    /** 
+    /**
      * A constructor for a PageIO when we know the offset of this page on disk
      */
     /* no qualifier */PageIO( long offset )
@@ -179,6 +179,45 @@ import org.apache.directory.mavibot.btre
     }
 
 
+    /* no qualifier */PageIO copy( PageIO copy )
+    {
+        // The data
+        if ( data.isDirect() )
+        {
+            copy.data = ByteBuffer.allocateDirect( data.capacity() );
+        }
+        else
+        {
+            copy.data = ByteBuffer.allocate( data.capacity() );
+        }
+
+        // Save the original buffer position and limit
+        int start = data.position();
+        int limit = data.limit();
+
+        // The data is extended to get all the bytes in it
+        data.position( 0 );
+        data.limit( data.capacity() );
+
+        // Copy the data
+        copy.data.put( data );
+
+        // Restore the original buffer to the initial position and limit
+        data.position( start );
+        data.limit( limit );
+
+        // Set those position and limit in the copied buffer
+        copy.data.position( start );
+        copy.data.limit( limit );
+
+        // The size
+        copy.size = size;
+
+        // The offset and next page pointers are not copied.
+        return copy;
+    }
+
+
     /**
      * @see Object#toString()
      */
@@ -186,7 +225,7 @@ import org.apache.directory.mavibot.btre
     {
         StringBuilder sb = new StringBuilder();
 
-        sb.append( "PageIO[offset:" ).append( offset );
+        sb.append( "PageIO[offset:0x" ).append( Long.toHexString( offset ) );
 
         if ( size != -1 )
         {
@@ -195,7 +234,7 @@ import org.apache.directory.mavibot.btre
 
         if ( nextPage != -1L )
         {
-            sb.append( ", next:" ).append( nextPage );
+            sb.append( ", next:0x" ).append( Long.toHexString( nextPage ) );
         }
 
         sb.append( "]" );

Modified: directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/PersistedBTree.java
URL: http://svn.apache.org/viewvc/directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/PersistedBTree.java?rev=1595477&r1=1595476&r2=1595477&view=diff
==============================================================================
--- directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/PersistedBTree.java (original)
+++ directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/PersistedBTree.java Sat May 17 13:31:23 2014
@@ -24,9 +24,7 @@ import java.io.Closeable;
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.nio.channels.FileChannel;
-import java.util.Map;
 import java.util.concurrent.ConcurrentLinkedQueue;
-import java.util.concurrent.locks.ReentrantLock;
 
 import net.sf.ehcache.Cache;
 import net.sf.ehcache.Status;
@@ -50,47 +48,44 @@ public class PersistedBTree<K, V> extend
     /** The LoggerFactory used by this class */
     protected static final Logger LOG = LoggerFactory.getLogger( PersistedBTree.class );
 
-    /** The RecordManager if the BTree is managed */
-    private RecordManager recordManager;
+    protected static final Logger LOG_PAGES = LoggerFactory.getLogger( "LOG_PAGES" );
 
-    /** The cache associated with this BTree */
+    /** The cache associated with this B-tree */
     protected Cache cache;
 
     /** The default number of pages to keep in memory */
-    static final int DEFAULT_CACHE_SIZE = 1000;
+    public static final int DEFAULT_CACHE_SIZE = 1000;
 
     /** The cache size, default to 1000 elements */
     protected int cacheSize = DEFAULT_CACHE_SIZE;
 
-    /** A flag indicating if this BTree is a Sub BTree */
-    private boolean isSubBtree = false;
-
-    /** The number of stored Values before we switch to a BTree */
+    /** The number of stored Values before we switch to a B-tree */
     private static final int DEFAULT_VALUE_THRESHOLD_UP = 8;
 
     /** The number of stored Values before we switch back to an array */
     private static final int DEFAULT_VALUE_THRESHOLD_LOW = 1;
 
-    /** The configuration for the array <-> BTree switch */
+    /** The configuration for the array <-> B-tree switch */
     /*No qualifier*/static int valueThresholdUp = DEFAULT_VALUE_THRESHOLD_UP;
     /*No qualifier*/static int valueThresholdLow = DEFAULT_VALUE_THRESHOLD_LOW;
 
-    /** A lock to protect the creation of the transaction */
-    protected ReentrantLock createTransaction = new ReentrantLock();
-
+    /** The BtreeInfo offset */
+    private long btreeInfoOffset;
+    
+    /** The internal recordManager */
+    private RecordManager recordManager;
 
     /**
      * Creates a new BTree, with no initialization.
      */
     /* no qualifier */PersistedBTree()
     {
-        btreeHeader = new BTreeHeader();
         setType( BTreeTypeEnum.PERSISTED );
     }
 
 
     /**
-     * Creates a new persisted BTree using the BTreeConfiguration to initialize the
+     * Creates a new persisted B-tree using the BTreeConfiguration to initialize the
      * BTree
      *
      * @param configuration The configuration to use
@@ -105,22 +100,16 @@ public class PersistedBTree<K, V> extend
             throw new IllegalArgumentException( "BTree name cannot be null" );
         }
 
-        btreeHeader = new BTreeHeader();
-        btreeHeader.setName( name );
-        btreeHeader.setPageSize( configuration.getPageSize() );
-        isSubBtree = configuration.isSubBtree();
-
-        keySerializer = configuration.getKeySerializer();
-        btreeHeader.setKeySerializerFQCN( keySerializer.getClass().getName() );
-
-        valueSerializer = configuration.getValueSerializer();
-        btreeHeader.setValueSerializerFQCN( valueSerializer.getClass().getName() );
+        setName( name );
+        setPageSize( configuration.getPageSize() );
+        setKeySerializer( configuration.getKeySerializer() );
+        setValueSerializer( configuration.getValueSerializer() );
+        setAllowDuplicates( configuration.isAllowDuplicates() );
+        setType( configuration.getBtreeType() );
 
         readTimeOut = configuration.getReadTimeOut();
         writeBufferSize = configuration.getWriteBufferSize();
-        btreeHeader.setAllowDuplicates( configuration.isAllowDuplicates() );
         cacheSize = configuration.getCacheSize();
-        setType( BTreeTypeEnum.PERSISTED );
 
         if ( keySerializer.getComparator() == null )
         {
@@ -129,18 +118,35 @@ public class PersistedBTree<K, V> extend
 
         // Create the first root page, with revision 0L. It will be empty
         // and increment the revision at the same time
-        rootPage = new PersistedLeaf<K, V>( this );
+        Page<K, V> rootPage = new PersistedLeaf<K, V>( this );
 
-        if ( isSubBtree )
-        {
-            // The subBTree inherit its cache from its parent BTree
-            this.cache = ( ( PersistedBTree<K, V> ) configuration.getParentBTree() ).getCache();
-            this.writeLock = ( ( PersistedBTree<K, V> ) configuration.getParentBTree() ).getWriteLock();
-            readTransactions = new ConcurrentLinkedQueue<ReadTransaction<K, V>>();
+        // Create a B-tree header, and initialize it
+        BTreeHeader<K, V> btreeHeader = new BTreeHeader<K, V>();
+        btreeHeader.setRootPage( rootPage );
+        btreeHeader.setBtree( this );
+
+        switch ( btreeType )
+        {
+            case BTREE_OF_BTREES :
+            case COPIED_PAGES_BTREE :
+                // We will create a new cache and a new readTransactions map 
+                init( null );
+                currentBtreeHeader = btreeHeader;
+                break;
+
+            case PERSISTED_SUB :
+                init( ( PersistedBTree<K, V> ) configuration.getParentBTree() );
+                btreeRevisions.put( 0L, btreeHeader );
+                currentBtreeHeader = btreeHeader;
+                break;
+                
+            default :
+                // We will create a new cache and a new readTransactions map 
+                init( null );
+                btreeRevisions.put( 0L, btreeHeader );
+                currentBtreeHeader = btreeHeader;
+                break;
         }
-
-        // Now, initialize the BTree
-        init();
     }
 
 
@@ -149,17 +155,15 @@ public class PersistedBTree<K, V> extend
      *
      * @throws IOException If we get some exception while initializing the BTree
      */
-    public void init()
+    public void init( BTree<K, V> parentBTree )
     {
-        if ( !isSubBtree )
+        if ( parentBTree == null )
         {
             // This is not a subBtree, we have to initialize the cache
 
             // Create the queue containing the pending read transactions
             readTransactions = new ConcurrentLinkedQueue<ReadTransaction<K, V>>();
 
-            writeLock = new ReentrantLock();
-
             // Initialize the caches
             CacheConfiguration cacheConfiguration = new CacheConfiguration();
             cacheConfiguration.setName( "pages" );
@@ -172,6 +176,11 @@ public class PersistedBTree<K, V> extend
             cache = new Cache( cacheConfiguration );
             cache.initialise();
         }
+        else
+        {
+            this.cache = ((PersistedBTree<K, V>)parentBTree).getCache();
+            this.readTransactions = ((PersistedBTree<K, V>)parentBTree).getReadTransactions();
+        }
 
         // Initialize the txnManager thread
         //FIXME we should NOT create a new transaction manager thread for each BTree
@@ -191,15 +200,6 @@ public class PersistedBTree<K, V> extend
     /**
      * Return the cache we use in this BTree
      */
-    /* No qualifier */ReentrantLock getWriteLock()
-    {
-        return writeLock;
-    }
-
-
-    /**
-     * Return the cache we use in this BTree
-     */
     /* No qualifier */ConcurrentLinkedQueue<ReadTransaction<K, V>> getReadTransactions()
     {
         return readTransactions;
@@ -222,8 +222,6 @@ public class PersistedBTree<K, V> extend
         }
 
         cache.dispose();
-
-        rootPage = null;
     }
 
 
@@ -232,16 +230,16 @@ public class PersistedBTree<K, V> extend
      */
     /* No qualifier*/long getBtreeOffset()
     {
-        return btreeHeader.getBTreeOffset();
+        return getBTreeHeader( getName() ).getBTreeHeaderOffset();
     }
 
 
     /**
-     * @param btreeOffset the btreeOffset to set
+     * @param btreeOffset the B-tree header Offset to set
      */
-    /* No qualifier*/void setBtreeOffset( long btreeOffset )
+    /* No qualifier*/void setBtreeHeaderOffset( long btreeHeaderOffset )
     {
-        btreeHeader.setBTreeOffset( btreeOffset );
+        getBTreeHeader( getName() ).setBTreeHeaderOffset( btreeHeaderOffset );
     }
 
 
@@ -250,41 +248,14 @@ public class PersistedBTree<K, V> extend
      */
     /* No qualifier*/long getRootPageOffset()
     {
-        return btreeHeader.getRootPageOffset();
-    }
-
-
-    /**
-     * @param rootPageOffset the rootPageOffset to set
-     */
-    /* No qualifier*/void setRootPageOffset( long rootPageOffset )
-    {
-        btreeHeader.setRootPageOffset( rootPageOffset );
-    }
-
-
-    /**
-     * @return the nextBTreeOffset
-     */
-    /* No qualifier*/long getNextBTreeOffset()
-    {
-        return btreeHeader.getNextBTreeOffset();
-    }
-
-
-    /**
-     * @param nextBTreeOffset the nextBTreeOffset to set
-     */
-    /* No qualifier*/void setNextBTreeOffset( long nextBTreeOffset )
-    {
-        btreeHeader.setNextBTreeOffset( nextBTreeOffset );
+        return getBTreeHeader( getName() ).getRootPageOffset();
     }
 
 
     /**
      * Gets the RecordManager for a managed BTree
      *
-     * @return The recordManager if the BTree is managed
+     * @return The recordManager if the B-tree is managed
      */
     /* No qualifier */RecordManager getRecordManager()
     {
@@ -299,6 +270,8 @@ public class PersistedBTree<K, V> extend
      */
     /* No qualifier */void setRecordManager( RecordManager recordManager )
     {
+        // The RecordManager is also the TransactionManager
+        transactionManager = recordManager;
         this.recordManager = recordManager;
     }
 
@@ -315,81 +288,158 @@ public class PersistedBTree<K, V> extend
      * @return
      * @throws IOException
      */
-    protected Tuple<K, V> delete( K key, V value, long revision ) throws IOException
+    /* no qualifier */ Tuple<K, V> delete( K key, V value, long revision ) throws IOException
     {
-        writeLock.lock();
+        // We have to start a new transaction, which will be committed or rollbacked
+        // locally. This will duplicate the current BtreeHeader during this phase.
+        if ( revision == -1L )
+        {
+            revision = currentRevision.get() + 1;
+        }
 
         try
         {
-            // If the key exists, the existing value will be replaced. We store it
-            // to return it to the caller.
-            Tuple<K, V> tuple = null;
-
             // Try to delete the entry starting from the root page. Here, the root
             // page may be either a Node or a Leaf
-            DeleteResult<K, V> result = rootPage.delete( revision, key, value, null, -1 );
+            DeleteResult<K, V> result = processDelete( key, value, revision );
 
+            // Check that we have found the element to delete
             if ( result instanceof NotPresentResult )
             {
-                // Key not found.
+                // We haven't found the element in the B-tree, just get out
+                // without updating the recordManager
+
                 return null;
             }
 
-            // Keep the oldRootPage so that we can later access it
-            Page<K, V> oldRootPage = rootPage;
+            // The element was found, and removed
+            AbstractDeleteResult<K, V> deleteResult = ( AbstractDeleteResult<K, V> ) result;
 
-            if ( result instanceof RemoveResult )
-            {
-                // The element was found, and removed
-                RemoveResult<K, V> removeResult = ( RemoveResult<K, V> ) result;
+            Tuple<K, V> tuple = deleteResult.getRemovedElement();
 
-                Page<K, V> modifiedPage = removeResult.getModifiedPage();
+            // If the B-tree is managed, we have to update the rootPage on disk
+            // Update the RecordManager header
 
-                // Write the modified page on disk
-                // Note that we don't use the holder, the new root page will
-                // remain in memory.
-                PageHolder<K, V> holder = writePage( modifiedPage, revision );
-
-                // Store the offset on disk in the page in memory
-                ( ( AbstractPage<K, V> ) modifiedPage ).setOffset( ( ( PersistedPageHolder<K, V> ) holder )
-                    .getOffset() );
-
-                // Store the last offset on disk in the page in memory
-                ( ( AbstractPage<K, V> ) modifiedPage )
-                    .setLastOffset( ( ( PersistedPageHolder<K, V> ) holder )
-                        .getLastOffset() );
-
-                // This is a new root
-                rootPage = modifiedPage;
-                tuple = removeResult.getRemovedElement();
-            }
+            // Return the value we have found if it was modified
+            return tuple;
+        }
+        catch ( IOException ioe )
+        {
+            // if we've got an error, we have to rollback
+            throw ioe;
+        }
+    }
 
-            // Decrease the number of elements in the current tree if the deletion is successful
-            if ( tuple != null )
-            {
-                btreeHeader.decrementNbElems();
 
-                // We have to update the rootPage on disk
-                // Update the BTree header now
-                recordManager.updateBtreeHeader( this, ( ( AbstractPage<K, V> ) rootPage ).getOffset() );
-            }
+    /**
+     * Insert the tuple into the B-tree rootPage, get back the new rootPage
+     */
+    private DeleteResult<K, V> processDelete( K key, V value, long revision ) throws IOException
+    {
+        // Get the current B-tree header, and delete the value from it
+        BTreeHeader<K, V> btreeHeader = getBTreeHeader( getName() );
 
-            recordManager.addFreePages( this, result.getCopiedPages() );
+        // Try to delete the entry starting from the root page. Here, the root
+        // page may be either a Node or a Leaf
+        DeleteResult<K, V> result = btreeHeader.getRootPage().delete( key, value, revision);
 
-            // Update the RecordManager header
-            recordManager.updateRecordManagerHeader();
+        if ( result instanceof NotPresentResult )
+        {
+            // Key not found.
+            return result;
+        }
 
-            // Store the created rootPage into the revision BTree, this will be stored in RecordManager only if revisions are set to keep
-            recordManager.storeRootPage( this, rootPage );
+        // Create a new BTreeHeader
+        BTreeHeader<K, V> newBtreeHeader = btreeHeader.copy();
 
-            // Return the value we have found if it was modified
-            return tuple;
+        // Inject the old B-tree header into the pages to be freed
+        // if we are deleting an element from a management BTree
+        if ( ( btreeType == BTreeTypeEnum.BTREE_OF_BTREES ) || ( btreeType == BTreeTypeEnum.COPIED_PAGES_BTREE ) )
+        {
+            PageIO[] pageIos = recordManager.readPageIOs( btreeHeader.getBTreeHeaderOffset(), -1L );
+
+            for ( PageIO pageIo : pageIos )
+            {
+                recordManager.freedPages.add( pageIo );
+            }
         }
-        finally
+
+        // The element was found, and removed
+        AbstractDeleteResult<K, V> removeResult = ( AbstractDeleteResult<K, V> ) result;
+
+        // This is a new root
+        Page<K, V> newRootPage = removeResult.getModifiedPage();
+
+        // Write the modified page on disk
+        // Note that we don't use the holder, the new root page will
+        // remain in memory.
+        PageHolder<K, V> holder = writePage( newRootPage, revision );
+
+        // Decrease the number of elements in the current tree
+        newBtreeHeader.decrementNbElems();
+        newBtreeHeader.setRootPage( newRootPage );
+        newBtreeHeader.setRevision( revision );
+
+        // Write down the data on disk
+        long newBtreeHeaderOffset = recordManager.writeBtreeHeader( this, newBtreeHeader );
+
+
+        // Update the B-tree of B-trees with this new offset, if we are not already doing so
+        switch ( btreeType )
         {
-            // See above
-            writeLock.unlock();
+            case PERSISTED :
+                // We have a new B-tree header to inject into the B-tree of btrees
+                recordManager.addInBtreeOfBtrees( getName(), revision, newBtreeHeaderOffset );
+
+                recordManager.addInCopiedPagesBtree( getName(), revision, result.getCopiedPages() );
+
+                // Store the new revision
+                storeRevision( newBtreeHeader, recordManager.isKeepRevisions() );
+
+                break;
+                
+            case PERSISTED_SUB :
+                // Sub-B-trees are only updating the CopiedPage B-tree
+                recordManager.addInCopiedPagesBtree( getName(), revision, result.getCopiedPages() );
+                
+                //btreeRevisions.put( revision, newBtreeHeader );
+
+                currentRevision.set( revision );
+
+
+                break;
+
+            case BTREE_OF_BTREES :
+                // The B-tree of B-trees or the copiedPages B-tree has been updated, update the RMheader parameters
+                recordManager.updateRecordManagerHeader( newBtreeHeaderOffset, -1L );
+
+                // We can free the copied pages
+                recordManager.freePages( this, revision, result.getCopiedPages() );
+
+                // Store the new revision
+                storeRevision( newBtreeHeader, recordManager.isKeepRevisions() );
+
+                break;
+
+            case COPIED_PAGES_BTREE :
+                // The B-tree of B-trees or the copiedPages B-tree has been updated, update the RMheader parameters
+                recordManager.updateRecordManagerHeader( -1L, newBtreeHeaderOffset );
+
+                // We can free the copied pages
+                recordManager.freePages( this, revision, result.getCopiedPages() );
+
+                // Store the new revision
+                storeRevision( newBtreeHeader, recordManager.isKeepRevisions() );
+
+                break;
+
+            default:
+                // Nothing to do for sub-btrees
+                break;
         }
+
+        // Return the value we have found if it was modified
+        return result;
     }
 
 
@@ -408,36 +458,79 @@ public class PersistedBTree<K, V> extend
      */
     /* no qualifier */InsertResult<K, V> insert( K key, V value, long revision ) throws IOException
     {
-        if ( key == null )
+        // We have to start a new transaction, which will be committed or rollbacked
+        // locally. This will duplicate the current BtreeHeader during this phase.
+        if ( revision == -1L )
+        {
+            revision = currentRevision.get() + 1;
+        }
+
+        try
+        {
+            // Try to insert the new value in the tree at the right place,
+            // starting from the root page. Here, the root page may be either
+            // a Node or a Leaf
+            InsertResult<K, V> result = processInsert( key, value, revision );
+
+            // Return the value we have found if it was modified
+            return result;
+        }
+        catch ( IOException ioe )
         {
-            throw new IllegalArgumentException( "Key must not be null" );
+            throw ioe;
         }
+    }
 
-        // If the key exists, the existing value will be replaced. We store it
-        // to return it to the caller.
-        V modifiedValue = null;
+    
+    private BTreeHeader<K, V> getBTreeHeader( String name )
+    {
+        if ( btreeType == BTreeTypeEnum.PERSISTED_SUB )
+        {
+            return getBtreeHeader();
+        }
+        
+        BTreeHeader<K, V> btreeHeader = recordManager.getBTreeHeader( getName() );
 
-        // Try to insert the new value in the tree at the right place,
-        // starting from the root page. Here, the root page may be either
-        // a Node or a Leaf
-        InsertResult<K, V> result = rootPage.insert( revision, key, value );
+        return btreeHeader;
+    }
+
+    /**
+     * Insert the tuple into the B-tree rootPage, get back the new rootPage
+     */
+    private InsertResult<K, V> processInsert( K key, V value, long revision ) throws IOException
+    {
+        // Get the current B-tree header, and insert the value into it
+        BTreeHeader<K, V> btreeHeader = getBTreeHeader( getName() );
+        InsertResult<K, V> result = btreeHeader.getRootPage().insert( key, value, revision );
+
+        // Create a new BTreeHeader
+        BTreeHeader<K, V> newBtreeHeader = btreeHeader.copy();
+
+        // Inject the old B-tree header into the pages to be freed
+        // if we are inserting an element in a management BTree
+        if ( ( btreeType == BTreeTypeEnum.BTREE_OF_BTREES ) || ( btreeType == BTreeTypeEnum.COPIED_PAGES_BTREE ) )
+        {
+            PageIO[] pageIos = recordManager.readPageIOs( btreeHeader.getBTreeHeaderOffset(), -1L );
+
+            for ( PageIO pageIo : pageIos )
+            {
+                recordManager.freedPages.add( pageIo );
+            }
+        }
+
+        Page<K, V> newRootPage;
 
         if ( result instanceof ModifyResult )
         {
             ModifyResult<K, V> modifyResult = ( ( ModifyResult<K, V> ) result );
 
-            Page<K, V> modifiedPage = modifyResult.getModifiedPage();
+            newRootPage = modifyResult.getModifiedPage();
 
-            // Write the modified page on disk
-            // Note that we don't use the holder, the new root page will
-            // remain in memory.
-            writePage( modifiedPage, revision );
-
-            // The root has just been modified, we haven't split it
-            // Get it and make it the current root page
-            rootPage = modifiedPage;
-
-            modifiedValue = modifyResult.getModifiedValue();
+            // Increment the counter if we have inserted a new value
+            if ( modifyResult.getModifiedValue() == null )
+            {
+                newBtreeHeader.incrementNbElems();
+            }
         }
         else
         {
@@ -448,9 +541,8 @@ public class PersistedBTree<K, V> extend
             K pivot = splitResult.getPivot();
             Page<K, V> leftPage = splitResult.getLeftPage();
             Page<K, V> rightPage = splitResult.getRightPage();
-            Page<K, V> newRootPage = null;
 
-            // If the BTree is managed, we have to write the two pages that were created
+            // If the B-tree is managed, we have to write the two pages that were created
             // and to keep a track of the two offsets for the upper node
             PageHolder<K, V> holderLeft = writePage( leftPage, revision );
 
@@ -459,41 +551,81 @@ public class PersistedBTree<K, V> extend
             // Create the new rootPage
             newRootPage = new PersistedNode<K, V>( this, revision, pivot, holderLeft, holderRight );
 
-            // If the BTree is managed, we now have to write the page on disk
-            // and to add this page to the list of modified pages
-            PageHolder<K, V> holder = writePage( newRootPage, revision );
-
-            rootPage = newRootPage;
+            // Always increment the counter : we have added a new value
+            newBtreeHeader.incrementNbElems();
         }
 
-        // Increase the number of element in the current tree if the insertion is successful
-        // and does not replace an element
-        if ( modifiedValue == null )
-        {
-            btreeHeader.incrementNbElems();
-        }
+        // Write the new root page on disk
+        LOG_PAGES.debug( "Writing the new rootPage revision {} for {}", revision, name );
+        writePage( newRootPage, revision );
+
+        // Update the new B-tree header
+        newBtreeHeader.setRootPage( newRootPage );
+        newBtreeHeader.setRevision( revision );
 
-        // If the BTree is managed, we have to update the rootPage on disk
-        // Update the RecordManager header
-        if ( ( writeTransaction == null ) || !writeTransaction.isStarted() )
+        // Write down the data on disk
+        long newBtreeHeaderOffset = recordManager.writeBtreeHeader( this, newBtreeHeader );
+
+        // Update the B-tree of B-trees with this new offset, if we are not already doing so
+        switch ( btreeType )
         {
-            recordManager.updateRecordManagerHeader();
+            case PERSISTED :
+                // We have a new B-tree header to inject into the B-tree of btrees
+                recordManager.addInBtreeOfBtrees( getName(), revision, newBtreeHeaderOffset );
+
+                recordManager.addInCopiedPagesBtree( getName(), revision, result.getCopiedPages() );
+
+                // Store the new revision
+                storeRevision( newBtreeHeader, recordManager.isKeepRevisions() );
+
+                break;
+
+            case PERSISTED_SUB :
+                // Sub-B-trees are only updating the CopiedPage B-tree
+                recordManager.addInCopiedPagesBtree( getName(), revision, result.getCopiedPages() );
+                
+                //btreeRevisions.put( revision, newBtreeHeader );
 
-            // Update the BTree header now
-            recordManager.updateBtreeHeader( this, ( ( AbstractPage<K, V> ) rootPage ).getOffset() );
+                // Store the new revision
+                storeRevision( newBtreeHeader, recordManager.isKeepRevisions() );
 
-            // Moved the free pages into the list of free pages
-            recordManager.addFreePages( this, result.getCopiedPages() );
+                currentRevision.set( revision );
 
-            // Store the created rootPage into the revision BTree, this will be stored in RecordManager only if revisions are set to keep
-            recordManager.storeRootPage( this, rootPage );
+                break;
+
+            case BTREE_OF_BTREES :
+                // The B-tree of B-trees or the copiedPages B-tree has been updated, update the RMheader parameters
+                recordManager.updateRecordManagerHeader( newBtreeHeaderOffset, -1L );
+
+                // We can free the copied pages
+                recordManager.freePages( this, revision, result.getCopiedPages() );
+
+                // Store the new revision
+                storeRevision( newBtreeHeader, recordManager.isKeepRevisions() );
+
+                break;
+
+            case COPIED_PAGES_BTREE :
+                // The B-tree of B-trees or the copiedPages B-tree has been updated, update the RMheader parameters
+                recordManager.updateRecordManagerHeader( -1L, newBtreeHeaderOffset );
+
+                // We can free the copied pages
+                recordManager.freePages( this, revision, result.getCopiedPages() );
+
+                // Store the new revision
+                storeRevision( newBtreeHeader, recordManager.isKeepRevisions() );
+
+                break;
+
+            default:
+                // Nothing to do for sub-btrees
+                break;
         }
 
-        // Return the value we have found if it was modified
+        // Get the new root page and make it the current root page
         return result;
     }
 
-
     /**
      * Write the data in the ByteBuffer, and eventually on disk if needed.
      *
@@ -541,21 +673,9 @@ public class PersistedBTree<K, V> extend
      */
     private PageHolder<K, V> writePage( Page<K, V> modifiedPage, long revision ) throws IOException
     {
-        if ( ( writeTransaction != null ) && writeTransaction.isStarted() )
-        {
-            Map<Page<?, ?>, BTree<?, ?>> pendingPages = recordManager.getPendingPages();
-            pendingPages.put( modifiedPage, this );
-
-            PageHolder<K, V> pageHolder = new PageHolder<K, V>( this, modifiedPage );
-
-            return pageHolder;
-        }
-        else
-        {
-            PageHolder<K, V> pageHolder = recordManager.writePage( this, modifiedPage, revision );
+        PageHolder<K, V> pageHolder = recordManager.writePage( this, modifiedPage, revision );
 
-            return pageHolder;
-        }
+        return pageHolder;
     }
 
     /**
@@ -573,60 +693,76 @@ public class PersistedBTree<K, V> extend
 
 
     /**
-     * Starts a transaction
+     * Get the current rootPage
+     *
+     * @return The rootPage
      */
-    public void beginTransaction()
+    public Page<K, V> getRootPage()
     {
-        createTransaction.lock();
-
-        if ( writeTransaction == null )
-        {
-            writeTransaction = new WriteTransaction( recordManager );
-        }
+        return getBTreeHeader( getName() ).getRootPage();
+    }
 
-        createTransaction.unlock();
 
-        writeTransaction.start();
+    /* no qualifier */void setRootPage( Page<K, V> root )
+    {
+        getBTreeHeader( getName() ).setRootPage( root );
     }
 
 
     /**
-     * Commits a transaction
+     * @return the btreeInfoOffset
      */
-    public void commit()
+    public long getBtreeInfoOffset()
     {
-        createTransaction.lock();
-
-        if ( writeTransaction == null )
-        {
-            writeTransaction = new WriteTransaction( recordManager );
-        }
+        return btreeInfoOffset;
+    }
 
-        createTransaction.unlock();
 
-        writeTransaction.commit();
+    /**
+     * @param btreeInfoOffset the btreeInfoOffset to set
+     */
+    public void setBtreeInfoOffset( long btreeInfoOffset )
+    {
+        this.btreeInfoOffset = btreeInfoOffset;
     }
 
 
     /**
-     * Rollback a transaction
+     * {@inheritDoc}
      */
-    public void rollback()
+    protected ReadTransaction<K, V> beginReadTransaction()
     {
-        createTransaction.lock();
+        BTreeHeader<K, V> btreeHeader = getBTreeHeader( getName() );
 
-        if ( writeTransaction == null )
-        {
-            writeTransaction = new WriteTransaction( recordManager );
-        }
+        ReadTransaction<K, V> readTransaction = new ReadTransaction<K, V>( recordManager, btreeHeader, readTransactions );
 
-        createTransaction.unlock();
+        readTransactions.add( readTransaction );
 
-        writeTransaction.rollback();
+        return readTransaction;
     }
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    protected ReadTransaction<K, V> beginReadTransaction( long revision )
+    {
+        BTreeHeader<K, V> btreeHeader = getBtreeHeader( revision );
 
+        if ( btreeHeader != null )
+        {
+            ReadTransaction<K, V> readTransaction = new ReadTransaction<K, V>(  recordManager, btreeHeader, readTransactions );
 
+            readTransactions.add( readTransaction );
 
+            return readTransaction;
+        }
+        else
+        {
+            return null;
+        }
+    }
+    
 
     /**
      * @see Object#toString()
@@ -636,12 +772,12 @@ public class PersistedBTree<K, V> extend
         StringBuilder sb = new StringBuilder();
 
         sb.append( "Managed BTree" );
-        sb.append( "[" ).append( btreeHeader.getName() ).append( "]" );
-        sb.append( "( pageSize:" ).append( btreeHeader.getPageSize() );
+        sb.append( "[" ).append( getName() ).append( "]" );
+        sb.append( "( pageSize:" ).append( getPageSize() );
 
-        if ( rootPage != null )
+        if ( getBTreeHeader( getName() ).getRootPage() != null )
         {
-            sb.append( ", nbEntries:" ).append( btreeHeader.getNbElems() );
+            sb.append( ", nbEntries:" ).append( getBTreeHeader( getName() ).getNbElems() );
         }
         else
         {
@@ -659,10 +795,10 @@ public class PersistedBTree<K, V> extend
             sb.append( keySerializer.getComparator().getClass().getSimpleName() );
         }
 
-        sb.append( ", DuplicatesAllowed: " ).append( btreeHeader.isAllowDuplicates() );
+        sb.append( ", DuplicatesAllowed: " ).append( isAllowDuplicates() );
 
         sb.append( ") : \n" );
-        sb.append( rootPage.dumpPage( "" ) );
+        sb.append( getBTreeHeader( getName() ).getRootPage().dumpPage( "" ) );
 
         return sb.toString();
     }

Modified: directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/PersistedBTreeBuilder.java
URL: http://svn.apache.org/viewvc/directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/PersistedBTreeBuilder.java?rev=1595477&r1=1595476&r2=1595477&view=diff
==============================================================================
--- directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/PersistedBTreeBuilder.java (original)
+++ directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/PersistedBTreeBuilder.java Sat May 17 13:31:23 2014
@@ -32,7 +32,7 @@ import org.apache.directory.mavibot.btre
 
 
 /**
- * A BTree builder that builds a tree from the bottom.
+ * A B-tree builder that builds a tree from the bottom.
  *
  * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
  */
@@ -138,7 +138,7 @@ public class PersistedBTreeBuilder<K, V>
 
         rm.updateBtreeHeader( btree, ( ( AbstractPage<K, V> ) rootPage ).getOffset() );
 
-        rm.addFreePages( btree, Arrays.asList( btree.getRootPage() ) );
+        rm.freePages( btree, btree.getRootPage().getRevision(), Arrays.asList( btree.getRootPage() ) );
 
         ( ( AbstractBTree<K, V> ) btree ).setRootPage( rootPage );
 

Modified: directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/PersistedBTreeConfiguration.java
URL: http://svn.apache.org/viewvc/directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/PersistedBTreeConfiguration.java?rev=1595477&r1=1595476&r2=1595477&view=diff
==============================================================================
--- directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/PersistedBTreeConfiguration.java (original)
+++ directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/PersistedBTreeConfiguration.java Sat May 17 13:31:23 2014
@@ -25,11 +25,11 @@ import org.apache.directory.mavibot.btre
 
 /**
  * The B+Tree Configuration. This class can be used to store all the configurable
- * parameters used by the BTree class
- * 
+ * parameters used by the B-tree class
+ *
  * @param <K> The type for the keys
  * @param <V> The type for the stored values
- * 
+ *
  * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
  */
 public class PersistedBTreeConfiguration<K, V>
@@ -40,23 +40,23 @@ public class PersistedBTreeConfiguration
     /** The size of the buffer used to write data in disk */
     private int writeBufferSize = BTree.DEFAULT_WRITE_BUFFER_SIZE;
 
-    /** The Key and Value serializer used for this tree. If none is provided, 
-     * the BTree will deduce the serializer to use from the generic type, and
+    /** The Key and Value serializer used for this tree. If none is provided,
+     * the B-tree will deduce the serializer to use from the generic type, and
      * use the default Java serialization  */
     private ElementSerializer<K> keySerializer;
     private ElementSerializer<V> valueSerializer;
 
-    /** The BTree name */
+    /** The B-tree name */
     private String name;
 
-    /** The path where the BTree file will be stored. Default to the local 
+    /** The path where the B-tree file will be stored. Default to the local
      * temporary directory.
      */
     private String filePath;
 
-    /** 
+    /**
      * The maximum delay to wait before a revision is considered as unused.
-     * This delay is necessary so that a read that does not ends does not 
+     * This delay is necessary so that a read that does not ends does not
      * hold a revision in memory forever.
      * The default value is 10000 (10 seconds). If the value is 0 or below,
      * the delay is considered as infinite
@@ -66,13 +66,13 @@ public class PersistedBTreeConfiguration
     /** Flag to enable duplicate key support */
     private boolean allowDuplicates;
 
-    /** A flag set when the BTree is a sub btree */
-    private boolean isSubBtree = false;
+    /** The B-tree type */
+    private BTreeTypeEnum btreeType = BTreeTypeEnum.PERSISTED;
 
     /** The cache size, if it's <= 0, we don't have cache */
     private int cacheSize;
 
-    /** The inherited BTree if we create a sub BTree */
+    /** The inherited B-tree if we create a sub B-tree */
     private BTree<?, V> parentBTree;
 
 
@@ -224,10 +224,10 @@ public class PersistedBTreeConfiguration
 
     /**
      * enable duplicate key support
-     * 
+     *
      * @param allowDuplicates
-     * @throws IllegalStateException if the btree was already initialized or when tried to turn off duplicate suport on
-     * an existing btree containing duplicate keys
+     * @throws IllegalStateException if the B-tree was already initialized or when tried to turn off duplicate suport on
+     * an existing B-tree containing duplicate keys
      */
     public void setAllowDuplicates( boolean allowDuplicates )
     {
@@ -272,19 +272,19 @@ public class PersistedBTreeConfiguration
 
 
     /**
-     * @return True if this is a sub btree, false otherwise
+     * @return The BtreeType for this Btree
      */
-    public boolean isSubBtree()
+    public BTreeTypeEnum getBtreeType()
     {
-        return isSubBtree;
+        return btreeType;
     }
 
 
     /**
-     * @param isSubBtree True if the BTree will be a sub btree, false otherwise
+     * @param btreeType The BtreeType
      */
-    public void setSubBtree( boolean isSubBtree )
+    public void setBtreeType( BTreeTypeEnum btreeType )
     {
-        this.isSubBtree = isSubBtree;
+        this.btreeType = btreeType;
     }
 }

Modified: directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/PersistedLeaf.java
URL: http://svn.apache.org/viewvc/directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/PersistedLeaf.java?rev=1595477&r1=1595476&r2=1595477&view=diff
==============================================================================
--- directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/PersistedLeaf.java (original)
+++ directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/PersistedLeaf.java Sat May 17 13:31:23 2014
@@ -71,7 +71,7 @@ import org.apache.directory.mavibot.btre
     /**
      * {@inheritDoc}
      */
-    public InsertResult<K, V> insert( long revision, K key, V value ) throws IOException
+    public InsertResult<K, V> insert( K key, V value, long revision ) throws IOException
     {
         // Find the key into this leaf
         int pos = findPos( key );
@@ -116,7 +116,7 @@ import org.apache.directory.mavibot.btre
      * {@inheritDoc}
      */
     @SuppressWarnings("unchecked")
-    public DeleteResult<K, V> delete( long revision, K key, V value, Page<K, V> parent, int parentPos )
+    /* no qualifier */ DeleteResult<K, V> delete( K key, V value, long revision, Page<K, V> parent, int parentPos )
         throws IOException
     {
         // Check that the leaf is not empty
@@ -519,7 +519,7 @@ import org.apache.directory.mavibot.btre
         }
         else
         {
-            throw KEY_NOT_FOUND_EXCEPTION;
+            throw KeyNotFoundException.INSTANCE;
         }
     }
 
@@ -561,7 +561,7 @@ import org.apache.directory.mavibot.btre
         }
         else
         {
-            throw KEY_NOT_FOUND_EXCEPTION;
+            throw KeyNotFoundException.INSTANCE;
         }
     }
 

Modified: directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/PersistedNode.java
URL: http://svn.apache.org/viewvc/directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/PersistedNode.java?rev=1595477&r1=1595476&r2=1595477&view=diff
==============================================================================
--- directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/PersistedNode.java (original)
+++ directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/PersistedNode.java Sat May 17 13:31:23 2014
@@ -120,7 +120,7 @@ import java.util.List;
     /**
      * {@inheritDoc}
      */
-    public InsertResult<K, V> insert( long revision, K key, V value ) throws IOException
+    public InsertResult<K, V> insert( K key, V value, long revision ) throws IOException
     {
         // Find the key into this leaf
         int pos = findPos( key );
@@ -136,7 +136,7 @@ import java.util.List;
         Page<K, V> child = children[pos].getValue();
 
         // and insert the <K, V> into this child
-        InsertResult<K, V> result = child.insert( revision, key, value );
+        InsertResult<K, V> result = child.insert( key, value, revision );
 
         // Ok, now, we have injected the <K, V> tuple down the tree. Let's check
         // the result to see if we have to split the current page
@@ -567,7 +567,7 @@ import java.util.List;
     /**
      * {@inheritDoc}
      */
-    public DeleteResult<K, V> delete( long revision, K key, V value, Page<K, V> parent, int parentPos )
+    /* no qualifier */ DeleteResult<K, V> delete( K key, V value, long revision, Page<K, V> parent, int parentPos )
         throws IOException
     {
         // We first try to delete the element from the child it belongs to
@@ -582,12 +582,12 @@ import java.util.List;
         {
             index = -( pos + 1 );
             child = children[-pos].getValue();
-            deleteResult = child.delete( revision, key, value, this, -pos );
+            deleteResult = ((AbstractPage<K, V>)child).delete( key, value, revision, this, -pos );
         }
         else
         {
             child = children[pos].getValue();
-            deleteResult = child.delete( revision, key, value, this, pos );
+            deleteResult = ((AbstractPage<K, V>)child).delete( key, value, revision, this, pos );
         }
 
         // If the key is not present in the tree, we simply return

Modified: directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/PersistedValueHolder.java
URL: http://svn.apache.org/viewvc/directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/PersistedValueHolder.java?rev=1595477&r1=1595476&r2=1595477&view=diff
==============================================================================
--- directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/PersistedValueHolder.java (original)
+++ directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/PersistedValueHolder.java Sat May 17 13:31:23 2014
@@ -28,6 +28,7 @@ import java.util.UUID;
 import org.apache.directory.mavibot.btree.exception.BTreeAlreadyCreatedException;
 import org.apache.directory.mavibot.btree.exception.BTreeAlreadyManagedException;
 import org.apache.directory.mavibot.btree.exception.BTreeCreationException;
+import org.apache.directory.mavibot.btree.exception.KeyNotFoundException;
 import org.apache.directory.mavibot.btree.serializer.IntSerializer;
 import org.apache.directory.mavibot.btree.serializer.LongSerializer;
 
@@ -82,7 +83,7 @@ import org.apache.directory.mavibot.btre
 
     /**
      * Creates a new instance of a ValueHolder, containing Values. This constructor is called
-     * whe we need to create a new ValueHolder with deserialized values.
+     * when we need to create a new ValueHolder with deserialized values.
      *
      * @param parentBtree The parent BTree
      * @param values The Values stored in the ValueHolder
@@ -248,12 +249,13 @@ import org.apache.directory.mavibot.btre
             configuration.setName( UUID.randomUUID().toString() );
             configuration.setValueSerializer( valueSerializer );
             configuration.setParentBTree( parentBtree );
-            configuration.setSubBtree( true );
+            configuration.setBtreeType( BTreeTypeEnum.PERSISTED_SUB );
 
             valueBtree = BTreeFactory.createPersistedBTree( configuration );
 
             try
             {
+                // The sub-btree will not be added into the BOB.
                 parentBtree.getRecordManager().manage( valueBtree, RecordManager.INTERNAL_BTREE );
                 raw = null;
             }
@@ -419,6 +421,12 @@ import org.apache.directory.mavibot.btre
                 e.printStackTrace();
                 return null;
             }
+            catch ( KeyNotFoundException knfe )
+            {
+                // TODO Auto-generated catch block
+                knfe.printStackTrace();
+                return null;
+            }
         }
         else
         {
@@ -655,7 +663,7 @@ import org.apache.directory.mavibot.btre
         long offset = LongSerializer.deserialize( raw );
 
         // and reload the sub btree
-        valueBtree = parentBtree.getRecordManager().loadDupsBTree( offset );
+        valueBtree = parentBtree.getRecordManager().loadDupsBtree( offset, parentBtree );
     }
 
 

Modified: directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/ReadTransaction.java
URL: http://svn.apache.org/viewvc/directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/ReadTransaction.java?rev=1595477&r1=1595476&r2=1595477&view=diff
==============================================================================
--- directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/ReadTransaction.java (original)
+++ directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/ReadTransaction.java Sat May 17 13:31:23 2014
@@ -21,6 +21,7 @@ package org.apache.directory.mavibot.btr
 
 
 import java.util.Date;
+import java.util.concurrent.ConcurrentLinkedQueue;
 
 
 /**
@@ -35,7 +36,7 @@ import java.util.Date;
  * A Transaction can be hold for quite a long time, for instance while doing
  * a browse against a big BTree. At some point, transactions which are pending
  * for too long will be closed by the transaction manager.
- * 
+ *
  * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
  *
  * @param <K> The type for the Key
@@ -49,26 +50,54 @@ public class ReadTransaction<K, V>
     /** The date of creation */
     private long creationDate;
 
-    /** The revision on which we are having a transaction */
-    private volatile Page<K, V> root;
+    /** The associated B-tree header */
+    private BTreeHeader<K, V> btreeHeader;
 
     /** A flag used to tell if a transaction is closed or not */
     private volatile boolean closed;
-
-
+    
+    /** The list of read transactions being executed */
+    private ConcurrentLinkedQueue<ReadTransaction<K, V>> readTransactions;
+
+    /** The reference to the recordManager, if any */
+    private RecordManager recordManager;
+    
+    /**
+     * Creates a new transaction instance
+     *
+     * @param btreeHeader The BtreeHeader we will use for this read transaction
+     */
+    public ReadTransaction( RecordManager recordManager, BTreeHeader<K, V> btreeHeader, ConcurrentLinkedQueue<ReadTransaction<K, V>> readTransactions )
+    {
+        if ( btreeHeader != null )
+        {
+            this.revision = btreeHeader.getRevision();
+            this.creationDate = System.currentTimeMillis();
+            this.btreeHeader = btreeHeader;
+            this.recordManager = recordManager;
+            closed = false;
+        }
+        
+        this.readTransactions = readTransactions;
+    }
+    
+    
     /**
      * Creates a new transaction instance
-     * 
-     * @param root The associated root
-     * @param revision The revision this transaction is using
-     * @param creationDate The creation date for this transaction
-     */
-    public ReadTransaction( Page<K, V> root, long revision, long creationDate )
-    {
-        this.revision = revision;
-        this.creationDate = creationDate;
-        this.root = root;
-        closed = false;
+     *
+     * @param btreeHeader The BtreeHeader we will use for this read transaction
+     */
+    public ReadTransaction( BTreeHeader<K, V> btreeHeader, ConcurrentLinkedQueue<ReadTransaction<K, V>> readTransactions )
+    {
+        if ( btreeHeader != null )
+        {
+            this.revision = btreeHeader.getRevision();
+            this.creationDate = System.currentTimeMillis();
+            this.btreeHeader = btreeHeader;
+            closed = false;
+        }
+        
+        this.readTransactions = readTransactions;
     }
 
 
@@ -82,20 +111,20 @@ public class ReadTransaction<K, V>
 
 
     /**
-     * @return the associated root
+     * @return the creationDate
      */
-    public Page<K, V> getRoot()
+    public long getCreationDate()
     {
-        return root;
+        return creationDate;
     }
 
 
     /**
-     * @return the creationDate
+     * @return the btreeHeader
      */
-    public long getCreationDate()
+    public BTreeHeader<K, V> getBtreeHeader()
     {
-        return creationDate;
+        return btreeHeader;
     }
 
 
@@ -104,8 +133,18 @@ public class ReadTransaction<K, V>
      */
     public void close()
     {
-        root = null;
         closed = true;
+        
+        // Remove the transaction from the list of opened transactions
+        readTransactions.remove( this );
+        
+        // and push the 
+        if ( recordManager != null )
+        {
+            recordManager.releaseTransaction( this );
+        }
+        
+        // Now, get back the copied pages
     }