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/02/10 16:35:20 UTC
svn commit: r1566659 [4/6] - in
/directory/mavibot/branches/with-txns/mavibot: img/
src/main/java/org/apache/directory/mavibot/btree/
src/main/java/org/apache/directory/mavibot/btree/comparator/
src/main/java/org/apache/directory/mavibot/btree/exceptio...
Modified: directory/mavibot/branches/with-txns/mavibot/src/main/java/org/apache/directory/mavibot/btree/RecordManager.java
URL: http://svn.apache.org/viewvc/directory/mavibot/branches/with-txns/mavibot/src/main/java/org/apache/directory/mavibot/btree/RecordManager.java?rev=1566659&r1=1566658&r2=1566659&view=diff
==============================================================================
--- directory/mavibot/branches/with-txns/mavibot/src/main/java/org/apache/directory/mavibot/btree/RecordManager.java (original)
+++ directory/mavibot/branches/with-txns/mavibot/src/main/java/org/apache/directory/mavibot/btree/RecordManager.java Mon Feb 10 15:35:18 2014
@@ -26,18 +26,22 @@ import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.locks.ReentrantLock;
import org.apache.directory.mavibot.btree.exception.BTreeAlreadyManagedException;
import org.apache.directory.mavibot.btree.exception.BTreeCreationException;
import org.apache.directory.mavibot.btree.exception.EndOfFileExceededException;
+import org.apache.directory.mavibot.btree.exception.FileException;
import org.apache.directory.mavibot.btree.exception.FreePageException;
import org.apache.directory.mavibot.btree.exception.InvalidBTreeException;
+import org.apache.directory.mavibot.btree.exception.InvalidOffsetException;
import org.apache.directory.mavibot.btree.exception.KeyNotFoundException;
import org.apache.directory.mavibot.btree.exception.RecordManagerException;
import org.apache.directory.mavibot.btree.serializer.ElementSerializer;
@@ -50,8 +54,8 @@ import org.slf4j.LoggerFactory;
/**
- * The RecordManager is used to manage the file in which we will store the BTrees.
- * A RecordManager will manage more than one BTree.<br/>
+ * The RecordManager is used to manage the file in which we will store the B-trees.
+ * A RecordManager will manage more than one B-tree.<br/>
*
* It stores data in fixed size pages (default size is 512 bytes), which may be linked one to
* the other if the data we want to store is too big for a page.
@@ -72,7 +76,7 @@ public class RecordManager
/** The channel used to read and write data */
private FileChannel fileChannel;
- /** The number of stored BTrees */
+ /** The number of managed B-trees */
private int nbBtree;
/** The first and last free page */
@@ -86,25 +90,22 @@ public class RecordManager
public AtomicLong nbCreatedPages = new AtomicLong( 0 );
public AtomicLong nbReusedPages = new AtomicLong( 0 );
public AtomicLong nbUpdateRMHeader = new AtomicLong( 0 );
- public AtomicLong nbUpdateBTreeHeader = new AtomicLong( 0 );
+ public AtomicLong nbUpdateBtreeHeader = new AtomicLong( 0 );
public AtomicLong nbUpdatePageIOs = new AtomicLong( 0 );
/** The offset of the end of the file */
private long endOfFileOffset;
/**
- * A Btree used to manage the page that has been copied in a new version.
+ * A B-tree used to manage the page that has been copied in a new version.
* Those pages can be reclaimed when the associated version is dead.
**/
- private BTree<RevisionName, long[]> copiedPageBTree;
-
- /** A BTree used to store all the valid revisions for all the stored BTrees */
- private BTree<RevisionName, Long> revisionBTree;
+ private BTree<RevisionName, long[]> copiedPageBtree;
/** A constant for an offset on a non existing page */
private static final long NO_PAGE = -1L;
- /** The header page size */
+ /** The number of element we can store in a page */
private static final int PAGE_SIZE = 4;
/** The size of the link to next page */
@@ -118,14 +119,17 @@ public class RecordManager
/** The default page size */
private static final int DEFAULT_PAGE_SIZE = 512;
- /** The header size */
- private static int HEADER_SIZE = DEFAULT_PAGE_SIZE;
+ /** The minimal page size. Can't be below 64, as we have to store many thing sin the RMHeader */
+ private static final int MIN_PAGE_SIZE = 64;
+
+ /** The RecordManager header size */
+ private static int RECORD_MANAGER_HEADER_SIZE = DEFAULT_PAGE_SIZE;
- /** A global buffer used to store the header */
- private static ByteBuffer HEADER_BUFFER;
+ /** A global buffer used to store the RecordManager header */
+ private static ByteBuffer RECORD_MANAGER_HEADER_BUFFER;
- /** A static buffer used to store the header */
- private static byte[] HEADER_BYTES;
+ /** A static buffer used to store the RecordManager header */
+ private static byte[] RECORD_MANAGER_HEADER_BYTES;
/** The length of an Offset, as a negative value */
private static byte[] LONG_LENGTH = new byte[]
@@ -134,22 +138,12 @@ public class RecordManager
/** The RecordManager underlying page size. */
private int pageSize = DEFAULT_PAGE_SIZE;
- /** The set of managed BTrees */
- private Map<String, BTree<Object, Object>> managedBTrees;
-
- /** The offset on the last added BTree */
- private long lastAddedBTreeOffset = NO_PAGE;
+ /** The set of managed B-trees */
+ private Map<String, BTree<Object, Object>> managedBtrees;
/** The default file name */
private static final String DEFAULT_FILE_NAME = "mavibot.db";
- /** A deserializer for Offsets */
- private static final LongSerializer OFFSET_SERIALIZER = new LongSerializer();
-
- private static final String REVISION_BTREE_NAME = "_revisionBTree_";
-
- private static final String COPIED_PAGE_BTREE_NAME = "_copiedPageBTree_";
-
/** A flag set to true if we want to keep old revisions */
private boolean keepRevisions;
@@ -159,17 +153,38 @@ public class RecordManager
/** A flag used by internal btrees */
public static final boolean NORMAL_BTREE = false;
- /** A map of pending pages */
- private Map<Page<?, ?>, BTree<?, ?>> pendingPages = new LinkedHashMap<Page<?, ?>, BTree<?, ?>>();
-
- /** The Btree of Btrees */
+ /** The B-tree of B-trees */
private BTree<NameRevision, Long> btreeOfBtrees;
- private static final String BOB_ONE_NAME = "_BTREE_OF_BTREES_";
+ /** The B-tree of B-trees management btree name */
+ private static final String BTREE_OF_BTREES_NAME = "_btree_of_btrees_";
+
+ /** The CopiedPages management btree name */
+ private static final String COPIED_PAGE_BTREE_NAME = "_copiedPageBtree_";
+
+ /** The current B-tree of B-trees header offset */
+ private long currentBtreeOfBtreesOffset;
+
+ /** The previous B-tree of B-trees header offset */
+ private long previousBtreeOfBtreesOffset = NO_PAGE;
+
+ /** The offset on the current copied pages B-tree */
+ private long currentCopiedPagesBtreeOffset = NO_PAGE;
- /** The two latest revisions of the BOB */
- private long bobCurrentRevision;
- private long bobOldRevision;
+ /** The offset on the previous copied pages B-tree */
+ private long previousCopiedPagesBtreeOffset = NO_PAGE;
+
+ /** The current transaction */
+ private WriteTransaction writeTransaction;
+
+ /** A lock to protect the creation of the transaction */
+ protected ReentrantLock createTransaction = new ReentrantLock();
+
+ /** The list of PageIO that can be freed after a commit */
+ private List<PageIO> freedPages = new ArrayList<PageIO>();
+
+ /** The list of PageIO that can be freed after a roolback */
+ private List<PageIO> allocatedPages = new ArrayList<PageIO>();
/**
* Create a Record manager which will either create the underlying file
@@ -190,15 +205,24 @@ public class RecordManager
* a file with a default name : mavibot.db
*
* @param name The file name, or a folder name
- * @param pageSize the size of a page on disk
+ * @param pageSize the size of a page on disk, in bytes
*/
public RecordManager( String fileName, int pageSize )
{
- managedBTrees = new LinkedHashMap<String, BTree<Object, Object>>();
+ managedBtrees = new LinkedHashMap<String, BTree<Object, Object>>();
+
+ if ( pageSize < MIN_PAGE_SIZE )
+ {
+ this.pageSize = MIN_PAGE_SIZE;
+ }
+ else
+ {
+ this.pageSize = pageSize;
+ }
- HEADER_BUFFER = ByteBuffer.allocate( pageSize );
- HEADER_BYTES = new byte[pageSize];
- HEADER_SIZE = pageSize;
+ RECORD_MANAGER_HEADER_BUFFER = ByteBuffer.allocate( this.pageSize );
+ RECORD_MANAGER_HEADER_BYTES = new byte[this.pageSize];
+ RECORD_MANAGER_HEADER_SIZE = this.pageSize;
// Open the file or create it
File tmpFile = new File( fileName );
@@ -222,7 +246,6 @@ public class RecordManager
if ( isNewFile )
{
- this.pageSize = pageSize;
initRecordManager();
}
else
@@ -264,33 +287,35 @@ public class RecordManager
LOG.error( "Cannot create the file {}", mavibotFile.getName() );
return false;
}
-
}
+
/**
- * We will create a brand new RecordManager file, containing nothing, but the header,
- * a BTree to manage the old revisions we want to keep and
- * a BTree used to manage pages associated with old versions.
+ * We will create a brand new RecordManager file, containing nothing, but the RecordManager header,
+ * a B-tree to manage the old revisions we want to keep and
+ * a B-tree used to manage pages associated with old versions.
* <br/>
- * The Header contains the following details :
+ * The RecordManager header contains the following details :
* <pre>
- * +---------------+
- * | PageSize | 4 bytes : The size of a physical page (default to 4096)
- * +---------------+
- * | NbTree | 4 bytes : The number of managed BTrees (at least 1)
- * +---------------+
- * | FirstFree | 8 bytes : The offset of the first free page
- * +---------------+
- * | currentBoB | 1 byte : The current BoB in use
- * +---------------+
- * | BoB offset[0] | 8 bytes : The offset of the first BoB
- * +---------------+
- * | BoB offset[1] | 8 bytes : The offset of the second BoB
- * +---------------+
+ * +--------------------------+
+ * | PageSize | 4 bytes : The size of a physical page (default to 4096)
+ * +--------------------------+
+ * | NbTree | 4 bytes : The number of managed B-trees (at least 1)
+ * +--------------------------+
+ * | FirstFree | 8 bytes : The offset of the first free page
+ * +--------------------------+
+ * | current BoB offset | 8 bytes : The offset of the current BoB
+ * +--------------------------+
+ * | previous BoB offset | 8 bytes : The offset of the previous BoB
+ * +--------------------------+
+ * | current CP btree offset | 8 bytes : The offset of the current BoB
+ * +--------------------------+
+ * | previous CP btree offset | 8 bytes : The offset of the previous BoB
+ * +--------------------------+
* </pre>
*
- * We then store the BTree managing the pages that have been copied when we have added
- * or deleted an element in the BTree. They are associated with a version.
+ * We then store the B-tree managing the pages that have been copied when we have added
+ * or deleted an element in the B-tree. They are associated with a version.
*
* Last, we add the bTree that keep a track on each revision we can have access to.
*/
@@ -299,8 +324,7 @@ public class RecordManager
// Create a new Header
nbBtree = 0;
firstFreePage = NO_PAGE;
- bobCurrentRevision = 0L;
- bobOldRevision = 0L;
+ currentBtreeOfBtreesOffset = 0L;
updateRecordManagerHeader();
@@ -308,22 +332,24 @@ public class RecordManager
endOfFileOffset = fileChannel.size();
// First, create the btree of btrees <NameRevision, Long>
- btreeOfBtrees = BTreeFactory.createPersistedBTree( BOB_ONE_NAME, new NameRevisionSerializer(),
- new LongSerializer() );
+ createBtreeOfBtrees();
- // Now, initialize the Copied Page BTree
- copiedPageBTree = BTreeFactory.createPersistedBTree( COPIED_PAGE_BTREE_NAME, new RevisionNameSerializer(),
- new LongArraySerializer() );
-
- // and initialize the Revision BTree
- revisionBTree = BTreeFactory.createPersistedBTree( REVISION_BTREE_NAME, new RevisionNameSerializer(),
- new LongSerializer() );
+ // Now, initialize the Copied Page B-tree
+ createCopiedPagesBtree();
- // Inject these BTrees into the RecordManager
+ // Inject these B-trees into the RecordManager. They are internal B-trees.
try
{
- manage( copiedPageBTree );
- manage( revisionBTree );
+ manage( btreeOfBtrees, INTERNAL_BTREE );
+
+ currentBtreeOfBtreesOffset = ((PersistedBTree<NameRevision, Long>)btreeOfBtrees).getBtreeOffset();
+ updateRecordManagerHeader();
+
+ // The FreePage B-tree
+ manage( copiedPageBtree, INTERNAL_BTREE );
+
+ currentCopiedPagesBtreeOffset = ((PersistedBTree<RevisionName, long[]>)copiedPageBtree).getBtreeOffset();
+ updateRecordManagerHeader();
}
catch ( BTreeAlreadyManagedException btame )
{
@@ -335,114 +361,276 @@ public class RecordManager
/**
+ * Create the B-treeOfBtrees
+ */
+ private void createBtreeOfBtrees()
+ {
+ PersistedBTreeConfiguration<NameRevision, Long> configuration = new PersistedBTreeConfiguration<NameRevision, Long>();
+ configuration.setKeySerializer( NameRevisionSerializer.INSTANCE );
+ configuration.setName( BTREE_OF_BTREES_NAME );
+ configuration.setValueSerializer( LongSerializer.INSTANCE );
+ configuration.setBtreeType( BTreeTypeEnum.PERSISTED_MANAGEMENT );
+
+ btreeOfBtrees = BTreeFactory.createPersistedBTree( configuration );
+ }
+
+
+ /**
+ * Create the CopiedPagesBtree
+ */
+ private void createCopiedPagesBtree()
+ {
+ PersistedBTreeConfiguration<RevisionName, long[]> configuration = new PersistedBTreeConfiguration<RevisionName, long[]>();
+ configuration.setKeySerializer( RevisionNameSerializer.INSTANCE );
+ configuration.setName( COPIED_PAGE_BTREE_NAME );
+ configuration.setValueSerializer( LongArraySerializer.INSTANCE );
+ configuration.setBtreeType( BTreeTypeEnum.PERSISTED_MANAGEMENT );
+
+ copiedPageBtree = BTreeFactory.createPersistedBTree( configuration );
+ }
+
+
+ /**
* Load the BTrees from the disk.
*
* @throws InstantiationException
* @throws IllegalAccessException
* @throws ClassNotFoundException
+ * @throws NoSuchFieldException
+ * @throws SecurityException
+ * @throws IllegalArgumentException
*/
private void loadRecordManager() throws IOException, ClassNotFoundException, IllegalAccessException,
- InstantiationException
+ InstantiationException, IllegalArgumentException, SecurityException, NoSuchFieldException
{
if ( fileChannel.size() != 0 )
{
- ByteBuffer header = ByteBuffer.allocate( HEADER_SIZE );
+ ByteBuffer recordManagerHeader = ByteBuffer.allocate( RECORD_MANAGER_HEADER_SIZE );
// The file exists, we have to load the data now
- fileChannel.read( header );
+ fileChannel.read( recordManagerHeader );
- header.rewind();
+ recordManagerHeader.rewind();
// read the RecordManager Header :
- // +----------------+
- // | PageSize | 4 bytes : The size of a physical page (default to 4096)
- // +----------------+
- // | NbTree | 4 bytes : The number of managed BTrees (at least 1)
- // +----------------+
- // | FirstFree | 8 bytes : The offset of the first free page
- // +----------------+
- // | BoB old offset | 8 bytes : The previous BoB revision
- // +----------------+
- // | BoB new offset | 8 bytes : The current BoB revision
- // +----------------+
+ // +---------------------+
+ // | PageSize | 4 bytes : The size of a physical page (default to 4096)
+ // +---------------------+
+ // | NbTree | 4 bytes : The number of managed B-trees (at least 1)
+ // +---------------------+
+ // | FirstFree | 8 bytes : The offset of the first free page
+ // +---------------------+
+ // | current BoB offset | 8 bytes : The offset of the current B-tree of B-trees
+ // +---------------------+
+ // | previous BoB offset | 8 bytes : The offset of the previous B-tree of B-trees
+ // +---------------------+
+ // | current CP offset | 8 bytes : The offset of the current Copied Pages B-tree
+ // +---------------------+
+ // | previous CP offset | 8 bytes : The offset of the previous Copied Pages B-tree
+ // +---------------------+
// The page size
- pageSize = header.getInt();
+ pageSize = recordManagerHeader.getInt();
- // The number of managed BTrees
- nbBtree = header.getInt();
+ // The number of managed B-trees
+ nbBtree = recordManagerHeader.getInt();
// The first and last free page
- firstFreePage = header.getLong();
+ firstFreePage = recordManagerHeader.getLong();
- // The BOB revisions
- long bobRevision1 = header.getLong();
- long bobRevision2 = header.getLong();
+ // The current BOB offset
+ currentBtreeOfBtreesOffset = recordManagerHeader.getLong();
- if ( bobRevision1 < bobRevision2 )
- {
- bobOldRevision = bobRevision1;
- bobCurrentRevision = bobRevision2;
- }
- else if ( bobRevision1 > bobRevision2 )
+ // The previous BOB offset
+ previousBtreeOfBtreesOffset = recordManagerHeader.getLong();
+
+ // The current Copied Pages B-tree offset
+ currentCopiedPagesBtreeOffset = recordManagerHeader.getLong();
+
+ // The previous Copied Pages B-tree offset
+ previousCopiedPagesBtreeOffset = recordManagerHeader.getLong();
+
+ // read the B-tree of B-trees
+ PageIO[] bobPageIos = readPageIOs( currentBtreeOfBtreesOffset, Long.MAX_VALUE );
+
+ btreeOfBtrees = BTreeFactory.<NameRevision, Long> createPersistedBTree();
+ ( ( PersistedBTree<NameRevision, Long> ) btreeOfBtrees ).setBtreeOffset( currentBtreeOfBtreesOffset );
+ loadBtree( bobPageIos, btreeOfBtrees );
+
+ // read the copied page B-tree
+ PageIO[] copiedPagesPageIos = readPageIOs( currentCopiedPagesBtreeOffset, Long.MAX_VALUE );
+
+ copiedPageBtree = BTreeFactory.<RevisionName, long[]> createPersistedBTree();
+ ( ( PersistedBTree<RevisionName, long[]> ) copiedPageBtree ).setBtreeOffset( currentCopiedPagesBtreeOffset );
+ loadBtree( copiedPagesPageIos, copiedPageBtree );
+
+ // Now, read all the B-trees from the btree of btrees
+ TupleCursor<NameRevision, Long> btreeCursor = btreeOfBtrees.browse();
+ Map<String, Long> loadedBtrees = new HashMap<String, Long>();
+
+ // loop on all the btrees we have, and keep only the latest revision
+ long currentRevision = -1L;
+
+ while ( btreeCursor.hasNext() )
{
- bobOldRevision = bobRevision2;
- bobCurrentRevision = bobRevision1;
+ Tuple<NameRevision, Long> btreeTuple = btreeCursor.next();
+ NameRevision nameRevision = btreeTuple.getKey();
+ long btreeOffset = btreeTuple.getValue();
+ long revision = nameRevision.getValue();
+
+ // Check if we already have prcoessed this B-tree
+ Long loadedBtreeRevision = loadedBtrees.get( nameRevision.getName() );
+
+ if ( loadedBtreeRevision != null )
+ {
+ // The btree has already been loaded. The revision is necessarily higher
+ if ( revision > currentRevision )
+ {
+ // We have a newer revision : switch to the new revision (we keep the offset atm)
+ loadedBtrees.put( nameRevision.getName(), btreeOffset );
+ currentRevision = revision;
+ }
+ }
+ else
+ {
+ // This is a new B-tree
+ loadedBtrees.put( nameRevision.getName(), btreeOffset );
+ currentRevision = nameRevision.getRevision();
+ }
}
- else
+
+ // TODO : clean up the old revisions...
+
+
+ // Now, we can load the real btrees using the offsets
+ for ( String btreeName : loadedBtrees.keySet() )
{
- // Special case : the RecordManage has been shtudown correctly
- bobOldRevision = bobRevision1;
- bobCurrentRevision = bobRevision2;
+ long btreeOffset = loadedBtrees.get( btreeName );
+
+ PageIO[] btreePageIos = readPageIOs( btreeOffset, Long.MAX_VALUE );
+
+ BTree<?, ?> btree = BTreeFactory.<NameRevision, Long> createPersistedBTree();
+ ( ( PersistedBTree<NameRevision, Long> ) btree ).setBtreeOffset( btreeOffset );
+ loadBtree( btreePageIos, btree );
+
+ // Add the btree into the map of managed B-trees
+ managedBtrees.put( btreeName, ( BTree<Object, Object> ) btree );
}
- // Now read each BTree. The first one is the one which
- // manage the modified pages. Once read, we can discard all
- // the pages that are stored in it, as we have restarted
- // the RecordManager.
- long btreeOffset = HEADER_SIZE;
+ // We are done ! Let's finish with the last initialization parts
+ endOfFileOffset = fileChannel.size();
+ }
+ }
- PageIO[] pageIos = readPageIOs( HEADER_SIZE, Long.MAX_VALUE );
- // Create the BTree
- copiedPageBTree = BTreeFactory.<RevisionName, long[]> createPersistedBTree();
- ( ( PersistedBTree<RevisionName, long[]> ) copiedPageBTree ).setBtreeOffset( btreeOffset );
+ /**
+ * Starts a transaction
+ */
+ /*No Qualifier*/void beginTransaction()
+ {
+ createTransaction.lock();
- loadBTree( pageIos, copiedPageBTree );
- long nextBtreeOffset = ( ( PersistedBTree<RevisionName, long[]> ) copiedPageBTree ).getNextBTreeOffset();
+ if ( writeTransaction == null )
+ {
+ writeTransaction = new WriteTransaction();
+ }
- // And the Revision BTree
- pageIos = readPageIOs( nextBtreeOffset, Long.MAX_VALUE );
+ createTransaction.unlock();
- revisionBTree = BTreeFactory.<RevisionName, Long> createPersistedBTree();
- ( ( PersistedBTree<RevisionName, Long> ) revisionBTree ).setBtreeOffset( nextBtreeOffset );
+ writeTransaction.start();
+ }
- loadBTree( pageIos, revisionBTree );
- nextBtreeOffset = ( ( PersistedBTree<RevisionName, Long> ) revisionBTree ).getNextBTreeOffset();
- // Then process the next ones
- for ( int i = 2; i < nbBtree; i++ )
- {
- // Create the BTree
- BTree<Object, Object> btree = BTreeFactory.createPersistedBTree();
- ( ( PersistedBTree<Object, Object> ) btree ).setRecordManager( this );
- ( ( PersistedBTree<Object, Object> ) btree ).setBtreeOffset( nextBtreeOffset );
- lastAddedBTreeOffset = nextBtreeOffset;
+ /**
+ * Tells if a transaction has been started or not
+ */
+ /*No Qualifier*/boolean isTransactionStarted()
+ {
+ createTransaction.lock();
- // Read the associated pages
- pageIos = readPageIOs( nextBtreeOffset, Long.MAX_VALUE );
+ boolean started = ( writeTransaction != null ) && ( writeTransaction.isStarted() );
- // Load the BTree
- loadBTree( pageIos, btree );
- nextBtreeOffset = ( ( PersistedBTree<Object, Object> ) btree ).getNextBTreeOffset();
+ createTransaction.unlock();
+
+ return started;
+ }
- // Store it into the managedBtrees map
- managedBTrees.put( btree.getName(), btree );
+
+ /**
+ * Commits a transaction
+ */
+ public void commit()
+ {
+ createTransaction.lock();
+
+ if ( writeTransaction == null )
+ {
+ throw new RuntimeException( "Cannot commit a transaction which hasn't been started");
+ }
+
+ createTransaction.unlock();
+
+ updateRecordManagerHeader();
+
+ // We can now free pages
+ for ( PageIO pageIo : freedPages )
+ {
+ try
+ {
+ free( pageIo );
+ }
+ catch ( IOException ioe )
+ {
+ throw new RecordManagerException( ioe.getMessage() );
}
+ }
- // We are done ! Let's finish with the last initialization parts
- endOfFileOffset = fileChannel.size();
+ // Release the allocated and freed pages list
+ freedPages.clear();
+ allocatedPages.clear();
+
+ // And update the RMHeader again
+ updateRecordManagerHeader();
+
+ writeTransaction.commit();
+ }
+
+
+ /**
+ * Rollback a transaction
+ */
+ public void rollback()
+ {
+ createTransaction.lock();
+
+ if ( writeTransaction == null )
+ {
+ throw new RuntimeException( "Cannot rollback a transaction which hasn't been started");
}
+
+ createTransaction.unlock();
+
+ // We can now free allocated pages
+ for ( PageIO pageIo : allocatedPages )
+ {
+ try
+ {
+ free( pageIo );
+ }
+ catch ( IOException ioe )
+ {
+ throw new RecordManagerException( ioe.getMessage() );
+ }
+ }
+
+ // Release the allocated and freed pages list
+ freedPages.clear();
+ allocatedPages.clear();
+
+ // And update the RMHeader
+ updateRecordManagerHeader();
+
+ writeTransaction.rollback();
}
@@ -451,9 +639,10 @@ public class RecordManager
* the first page.
*
* @param position The position of the first page
+ * @param limit The maximum bytes to read. Set this value to -1 when the size is unknown.
* @return An array of pages
*/
- private PageIO[] readPageIOs( long position, long limit ) throws IOException, EndOfFileExceededException
+ /*no qualifier*/ PageIO[] readPageIOs( long position, long limit ) throws IOException, EndOfFileExceededException
{
LOG.debug( "Read PageIOs at position {}", position );
@@ -497,20 +686,42 @@ public class RecordManager
/**
- * Read a BTree from the disk. The meta-data are at the given position in the list of pages.
+ * Check the offset to be sure it's a valid one :
+ * <ul>
+ * <li>It's >= 0</li>
+ * <li>It's below the end of the file</li>
+ * <li>It's a multipl of the pageSize
+ * </ul>
+ * @param offset The offset to check
+ * @throws InvalidOffsetException If the offset is not valid
+ */
+ private void checkOffset( long offset )
+ {
+ if ( ( offset < 0 ) || ( offset > endOfFileOffset ) || ( ( offset % pageSize ) != 0 ) )
+ {
+ throw new InvalidOffsetException( "Bad Offset : " + offset );
+ }
+ }
+
+
+ /**
+ * Read a B-tree from the disk. The meta-data are at the given position in the list of pages.
*
* @param pageIos The list of pages containing the meta-data
- * @param btree The BTree we have to initialize
+ * @param btree The B-tree we have to initialize
* @throws InstantiationException
* @throws IllegalAccessException
* @throws ClassNotFoundException
+ * @throws NoSuchFieldException
+ * @throws SecurityException
+ * @throws IllegalArgumentException
*/
- private <K, V> void loadBTree( PageIO[] pageIos, BTree<K, V> btree ) throws EndOfFileExceededException,
- IOException, ClassNotFoundException, IllegalAccessException, InstantiationException
+ private <K, V> void loadBtree( PageIO[] pageIos, BTree<K, V> btree ) throws EndOfFileExceededException,
+ IOException, ClassNotFoundException, IllegalAccessException, InstantiationException, IllegalArgumentException, SecurityException, NoSuchFieldException
{
long dataPos = 0L;
- // The BTree current revision
+ // The B-tree current revision
long revision = readLong( pageIos, dataPos );
BTreeFactory.setRevision( btree, revision );
dataPos += LONG_SIZE;
@@ -520,17 +731,12 @@ public class RecordManager
BTreeFactory.setNbElems( btree, nbElems );
dataPos += LONG_SIZE;
- // The BTree rootPage offset
+ // The B-tree rootPage offset
long rootPageOffset = readLong( pageIos, dataPos );
BTreeFactory.setRootPageOffset( btree, rootPageOffset );
dataPos += LONG_SIZE;
- // The next BTree offset
- long nextBTreeOffset = readLong( pageIos, dataPos );
- BTreeFactory.setNextBTreeOffset( btree, nextBTreeOffset );
- dataPos += LONG_SIZE;
-
- // The BTree page size
+ // The B-tree page size
int btreePageSize = readInt( pageIos, dataPos );
BTreeFactory.setPageSize( btree, btreePageSize );
dataPos += INT_SIZE;
@@ -567,20 +773,16 @@ public class RecordManager
BTreeFactory.setValueSerializer( btree, valueSerializerFqcn );
- // The BTree allowDuplicates flag
+ // The B-tree allowDuplicates flag
int allowDuplicates = readInt( pageIos, dataPos );
( ( PersistedBTree<K, V> ) btree ).setAllowDuplicates( allowDuplicates != 0 );
dataPos += INT_SIZE;
- // Now, init the BTree
+ // Now, init the B-tree
btree.init();
( ( PersistedBTree<K, V> ) btree ).setRecordManager( this );
- // Now, load the rootPage, which can be a Leaf or a Node, depending
- // on the number of elements in the tree : if it's above the pageSize,
- // it's a Node, otherwise it's a Leaf
-
// Read the rootPage pages on disk
PageIO[] rootPageIos = readPageIOs( rootPageOffset, Long.MAX_VALUE );
@@ -591,20 +793,18 @@ public class RecordManager
}
- private <K, V> Page<K, V> readNode( BTree<K, V> btree, long offset, long revision, int nbElems ) throws IOException
- {
- Page<K, V> node = BTreeFactory.createNode( btree, revision, nbElems );
-
- // Read the rootPage pages on disk
- PageIO[] pageIos = readPageIOs( offset, Long.MAX_VALUE );
-
- return node;
- }
-
-
+ /**
+ * Deserialize a Page from a B-tree at a give position
+ *
+ * @param btree The B-tree we want to read a Page from
+ * @param offset The position in the file for this page
+ * @return The read page
+ * @throws EndOfFileExceededException If we have reached the end of the file while reading the page
+ */
public <K, V> Page<K, V> deserialize( BTree<K, V> btree, long offset ) throws EndOfFileExceededException,
IOException
{
+ checkOffset( offset );
PageIO[] rootPageIos = readPageIOs( offset, Long.MAX_VALUE );
Page<K, V> page = readPage( btree, rootPageIos );
@@ -616,6 +816,13 @@ public class RecordManager
}
+ /**
+ * Read a page from some PageIO for a given B-tree
+ * @param btree The B-tree we want to read a page for
+ * @param pageIos The PageIO containing the raw data
+ * @return The read Page if successful
+ * @throws IOException If the deserialization failed
+ */
private <K, V> Page<K, V> readPage( BTree<K, V> btree, PageIO[] pageIos ) throws IOException
{
// Deserialize the rootPage now
@@ -659,8 +866,7 @@ public class RecordManager
* Deserialize a Leaf from some PageIOs
*/
private <K, V> PersistedLeaf<K, V> readLeafKeysAndValues( BTree<K, V> btree, int nbElems, long revision,
- ByteBuffer byteBuffer,
- PageIO[] pageIos )
+ ByteBuffer byteBuffer, PageIO[] pageIos )
{
// Its a leaf, create it
PersistedLeaf<K, V> leaf = ( PersistedLeaf<K, V> ) BTreeFactory.createLeaf( btree, revision, nbElems );
@@ -717,8 +923,7 @@ public class RecordManager
* Deserialize a Node from some PageIos
*/
private <K, V> PersistedNode<K, V> readNodeKeysAndValues( BTree<K, V> btree, int nbElems, long revision,
- ByteBuffer byteBuffer,
- PageIO[] pageIos ) throws IOException
+ ByteBuffer byteBuffer, PageIO[] pageIos ) throws IOException
{
PersistedNode<K, V> node = ( PersistedNode<K, V> ) BTreeFactory.createNode( btree, revision, nbElems );
@@ -726,8 +931,8 @@ public class RecordManager
for ( int i = 0; i < nbElems; i++ )
{
// This is an Offset
- long offset = OFFSET_SERIALIZER.deserialize( byteBuffer );
- long lastOffset = OFFSET_SERIALIZER.deserialize( byteBuffer );
+ long offset = LongSerializer.INSTANCE.deserialize( byteBuffer );
+ long lastOffset = LongSerializer.INSTANCE.deserialize( byteBuffer );
PersistedPageHolder<K, V> valueHolder = new PersistedPageHolder<K, V>( btree, null, offset, lastOffset );
node.setValue( i, valueHolder );
@@ -747,8 +952,8 @@ public class RecordManager
}
// and read the last value, as it's a node
- long offset = OFFSET_SERIALIZER.deserialize( byteBuffer );
- long lastOffset = OFFSET_SERIALIZER.deserialize( byteBuffer );
+ long offset = LongSerializer.INSTANCE.deserialize( byteBuffer );
+ long lastOffset = LongSerializer.INSTANCE.deserialize( byteBuffer );
PersistedPageHolder<K, V> valueHolder = new PersistedPageHolder<K, V>( btree, null, offset, lastOffset );
node.setValue( nbElems, valueHolder );
@@ -759,6 +964,7 @@ public class RecordManager
/**
* Read a byte[] from pages.
+ *
* @param pageIos The pages we want to read the byte[] from
* @param position The position in the data stored in those pages
* @return The byte[] we have read
@@ -787,7 +993,6 @@ public class RecordManager
else
{
ByteBuffer bytes = ByteBuffer.allocate( length );
- int bytesPos = 0;
while ( length > 0 )
{
@@ -814,7 +1019,6 @@ public class RecordManager
pageData.reset();
pageNb++;
pagePos = LINK_SIZE;
- bytesPos += remaining;
pageData = pageIos[pageNb].getData();
length -= remaining;
remaining = pageData.capacity() - pagePos;
@@ -1014,153 +1218,74 @@ public class RecordManager
/**
- * Manage a BTree. The btree will be added and managed by this RecordManager. We will create a
- * new RootPage for this added BTree, which will contain no data.
- *
- * @param btree The new BTree to manage.
+ * Manage a B-tree. The btree will be added and managed by this RecordManager. We will create a
+ * new RootPage for this added B-tree, which will contain no data.<br/>
+ * This method is threadsafe.
+ *
+ * @param btree The new B-tree to manage.
+ * @throws BTreeAlreadyManagedException if the B-tree is already managed
+ * @throws IOException if there was a problem while accessing the file
*/
public synchronized <K, V> void manage( BTree<K, V> btree ) throws BTreeAlreadyManagedException, IOException
{
+ boolean inTransaction = isTransactionStarted();
+
+ if ( !inTransaction )
+ {
+ beginTransaction();
+ }
+
manage( ( BTree<Object, Object> ) btree, NORMAL_BTREE );
+
+ if ( !inTransaction )
+ {
+ commit();
+ }
}
/**
- * works the same as @see #manage(BTree) except the given tree will not be linked to top level trees that will be
- * loaded initially if the internalTree flag is set to true
+ * Managing a btree is a matter of storing an reference to the managed B-tree in the B-tree Of B-trees.
+ * We store a tuple of NameRevision (where revision is 0L) and a offset to the B-tree header.
+ * At the same time, we keep a track of the managed B-trees in a Map.
*
- * @param btree The new BTree to manage.
- * @param internalTree flag indicating if this is an internal tree
+ * @param btree The new B-tree to manage.
+ * @param treeType flag indicating if this is an internal tree
*
- * @throws BTreeAlreadyManagedException
+ * @throws BTreeAlreadyManagedException If the B-tree is already managed
* @throws IOException
*/
- public synchronized <K, V> void manage( BTree<K, V> btree, boolean internalTree )
- throws BTreeAlreadyManagedException,
- IOException
+ public synchronized <K, V> void manage( BTree<K, V> btree, boolean treeType )
+ throws BTreeAlreadyManagedException, IOException
{
- LOG.debug( "Managing the btree {} which is an internam tree : {}", btree.getName(), internalTree );
+ LOG.debug( "Managing the btree {} which is an internam tree : {}", btree.getName(), treeType );
BTreeFactory.setRecordManager( btree, this );
String name = btree.getName();
- if ( managedBTrees.containsKey( name ) )
+ if ( managedBtrees.containsKey( name ) )
{
- // There is already a BTree with this name in the recordManager...
- LOG.error( "There is already a BTree named '{}' managed by this recordManager", name );
+ // There is already a B-tree with this name in the recordManager...
+ LOG.error( "There is already a B-tree named '{}' managed by this recordManager", name );
throw new BTreeAlreadyManagedException( name );
}
- // Do not add the BTree if it's internal into the Map of managed btrees, otherwise we will
- // not discard it when reloading a page wth internal btrees
- if ( !internalTree )
- {
- managedBTrees.put( name, ( BTree<Object, Object> ) btree );
- }
-
- // We will add the newly managed BTree at the end of the header.
- byte[] btreeNameBytes = Strings.getBytesUtf8( name );
- byte[] keySerializerBytes = Strings.getBytesUtf8( btree.getKeySerializerFQCN() );
- byte[] valueSerializerBytes = Strings.getBytesUtf8( btree.getValueSerializerFQCN() );
-
- int bufferSize =
- INT_SIZE + // The name size
- btreeNameBytes.length + // The name
- INT_SIZE + // The keySerializerBytes size
- keySerializerBytes.length + // The keySerializerBytes
- INT_SIZE + // The valueSerializerBytes size
- valueSerializerBytes.length + // The valueSerializerBytes
- INT_SIZE + // The page size
- LONG_SIZE + // The revision
- LONG_SIZE + // the number of element
- LONG_SIZE + // the nextBtree offset
- LONG_SIZE + // The root offset
- INT_SIZE; // The allowDuplicates flag
-
- // Get the pageIOs we need to store the data. We may need more than one.
- PageIO[] pageIos = getFreePageIOs( bufferSize );
-
- // Store the BTree Offset into the BTree
- long btreeOffset = pageIos[0].getOffset();
- ( ( PersistedBTree<K, V> ) btree ).setBtreeOffset( btreeOffset );
-
- // Now store the BTree data in the pages :
- // - the BTree revision
- // - the BTree number of elements
- // - The RootPage offset
- // - The next Btree offset
- // - the BTree page size
- // - the BTree name
- // - the keySerializer FQCN
- // - the valueSerializer FQCN
- // - the flags that tell if the dups are allowed
- // Starts at 0
- long position = 0L;
-
- // The BTree current revision
- position = store( position, btree.getRevision(), pageIos );
-
- // The nb elems in the tree
- position = store( position, btree.getNbElems(), pageIos );
-
- // Serialize the BTree root page
- Page<K, V> rootPage = BTreeFactory.getRootPage( btree );
+ long btreeOffset = writeBtreeHeader( btree );
- PageIO[] rootPageIos = serializePage( btree, btree.getRevision(), rootPage );
-
- // Get the reference on the first page
- PageIO rootPageIo = rootPageIos[0];
-
- // Now, we can inject the BTree rootPage offset into the BTree header
- position = store( position, rootPageIo.getOffset(), pageIos );
- ( ( PersistedBTree<K, V> ) btree ).setRootPageOffset( rootPageIo.getOffset() );
- ( ( PersistedLeaf<K, V> ) rootPage ).setOffset( rootPageIo.getOffset() );
-
- // The next BTree Header offset (-1L, as it's a new BTree)
- position = store( position, NO_PAGE, pageIos );
-
- // The BTree page size
- position = store( position, btree.getPageSize(), pageIos );
-
- // The tree name
- position = store( position, btreeNameBytes, pageIos );
-
- // The keySerializer FQCN
- position = store( position, keySerializerBytes, pageIos );
-
- // The valueSerialier FQCN
- position = store( position, valueSerializerBytes, pageIos );
-
- // The allowDuplicates flag
- position = store( position, ( btree.isAllowDuplicates() ? 1 : 0 ), pageIos );
-
- // And flush the pages to disk now
- LOG.debug( "Flushing the newly managed '{}' btree header", btree.getName() );
- flushPages( pageIos );
- LOG.debug( "Flushing the newly managed '{}' btree rootpage", btree.getName() );
- flushPages( rootPageIos );
-
- // Now, if this added BTree is not the first BTree, we have to link it with the
- // latest added BTree
- if ( !internalTree )
+ // Now, if this is a new B-tree, add it to the B-tree of B-trees
+ if ( treeType != INTERNAL_BTREE )
{
- nbBtree++;
-
- if ( lastAddedBTreeOffset != NO_PAGE )
- {
- // We have to update the nextBtreeOffset from the previous BTreeHeader
- pageIos = readPageIOs( lastAddedBTreeOffset, LONG_SIZE + LONG_SIZE + LONG_SIZE + LONG_SIZE );
- store( LONG_SIZE + LONG_SIZE + LONG_SIZE, btreeOffset, pageIos );
+ // Add the btree into the map of managed B-trees
+ managedBtrees.put( name, ( BTree<Object, Object> ) btree );
- // Write the pages on disk
- LOG.debug( "Updated the previous btree pointer on the added BTree {}", btree.getName() );
- flushPages( pageIos );
- }
+ // We can safely increment the number of managed B-trees
+ nbBtree++;
- lastAddedBTreeOffset = btreeOffset;
+ // Create the new NameRevision
+ NameRevision nameRevision = new NameRevision( name, 0L );
- // Last, not least, update the number of managed BTrees in the header
- updateRecordManagerHeader();
+ // Inject it into the B-tree of B-tree
+ btreeOfBtrees.insert( nameRevision, btreeOffset );
}
if ( LOG_CHECK.isDebugEnabled() )
@@ -1352,7 +1477,7 @@ public class RecordManager
/**
- * Serialize a Leaf's Value. We store
+ * Serialize a Leaf's Value.
*/
private <K, V> int serializeLeafValue( PersistedLeaf<K, V> leaf, int pos, List<byte[]> serializedData )
throws IOException
@@ -1403,7 +1528,7 @@ public class RecordManager
serializedData.add( buffer );
dataSize += buffer.length;
- // the BTree offset
+ // the B-tree offset
buffer = LongSerializer.serialize( ( ( PersistedValueHolder<V> ) valueHolder ).getOffset() );
serializedData.add( buffer );
dataSize += buffer.length;
@@ -1464,131 +1589,417 @@ public class RecordManager
/**
- * Update the header, injecting the following data :
+ * Update the RecordManager header, injecting the following data :
+ *
* <pre>
- * +---------------+
- * | PageSize | 4 bytes : The size of a physical page (default to 4096)
- * +---------------+
- * | NbTree | 4 bytes : The number of managed BTrees (at least 1)
- * +---------------+
- * | FirstFree | 8 bytes : The offset of the first free page
- * +---------------+
- * | currentBoB | 1 byte : The current BoB in use
- * +---------------+
- * | BoB offset[0] | 8 bytes : The offset of the first BoB
- * +---------------+
- * | BoB offset[1] | 8 bytes : The offset of the second BoB
- * +---------------+
+ * +---------------------+
+ * | PageSize | 4 bytes : The size of a physical page (default to 4096)
+ * +---------------------+
+ * | NbTree | 4 bytes : The number of managed B-trees (at least 1)
+ * +---------------------+
+ * | FirstFree | 8 bytes : The offset of the first free page
+ * +---------------------+
+ * | current BoB offset | 8 bytes : The offset of the current B-tree of B-trees
+ * +---------------------+
+ * | previous BoB offset | 8 bytes : The offset of the previous B-tree of B-trees
+ * +---------------------+
+ * | current CP offset | 8 bytes : The offset of the current CopiedPages B-tree
+ * +---------------------+
+ * | previous CP offset | 8 bytes : The offset of the previous CopiedPages B-tree
+ * +---------------------+
* </pre>
*/
- public void updateRecordManagerHeader() throws IOException
+ public void updateRecordManagerHeader()
{
// The page size
- HEADER_BYTES[0] = ( byte ) ( pageSize >>> 24 );
- HEADER_BYTES[1] = ( byte ) ( pageSize >>> 16 );
- HEADER_BYTES[2] = ( byte ) ( pageSize >>> 8 );
- HEADER_BYTES[3] = ( byte ) ( pageSize );
-
- // The number of managed BTree (currently we have only one : the discardedPage BTree
- HEADER_BYTES[4] = ( byte ) ( nbBtree >>> 24 );
- HEADER_BYTES[5] = ( byte ) ( nbBtree >>> 16 );
- HEADER_BYTES[6] = ( byte ) ( nbBtree >>> 8 );
- HEADER_BYTES[7] = ( byte ) ( nbBtree );
+ int position = writeData( RECORD_MANAGER_HEADER_BYTES, 0, pageSize );
+
+ // The number of managed B-tree
+ position = writeData( RECORD_MANAGER_HEADER_BYTES, position, nbBtree );
// The first free page
- HEADER_BYTES[8] = ( byte ) ( firstFreePage >>> 56 );
- HEADER_BYTES[9] = ( byte ) ( firstFreePage >>> 48 );
- HEADER_BYTES[10] = ( byte ) ( firstFreePage >>> 40 );
- HEADER_BYTES[11] = ( byte ) ( firstFreePage >>> 32 );
- HEADER_BYTES[12] = ( byte ) ( firstFreePage >>> 24 );
- HEADER_BYTES[13] = ( byte ) ( firstFreePage >>> 16 );
- HEADER_BYTES[14] = ( byte ) ( firstFreePage >>> 8 );
- HEADER_BYTES[15] = ( byte ) ( firstFreePage );
-
- // The offset of the first BoB
- HEADER_BYTES[17] = ( byte ) ( bobOldRevision >>> 56 );
- HEADER_BYTES[18] = ( byte ) ( bobOldRevision >>> 48 );
- HEADER_BYTES[19] = ( byte ) ( bobOldRevision >>> 40 );
- HEADER_BYTES[20] = ( byte ) ( bobOldRevision >>> 32 );
- HEADER_BYTES[21] = ( byte ) ( bobOldRevision >>> 24 );
- HEADER_BYTES[22] = ( byte ) ( bobOldRevision >>> 16 );
- HEADER_BYTES[23] = ( byte ) ( bobOldRevision >>> 8 );
- HEADER_BYTES[24] = ( byte ) ( bobOldRevision );
-
- // The offset of the second BoB
- HEADER_BYTES[17] = ( byte ) ( bobCurrentRevision >>> 56 );
- HEADER_BYTES[18] = ( byte ) ( bobCurrentRevision >>> 48 );
- HEADER_BYTES[19] = ( byte ) ( bobCurrentRevision >>> 40 );
- HEADER_BYTES[20] = ( byte ) ( bobCurrentRevision >>> 32 );
- HEADER_BYTES[21] = ( byte ) ( bobCurrentRevision >>> 24 );
- HEADER_BYTES[22] = ( byte ) ( bobCurrentRevision >>> 16 );
- HEADER_BYTES[23] = ( byte ) ( bobCurrentRevision >>> 8 );
- HEADER_BYTES[24] = ( byte ) ( bobCurrentRevision );
-
- // Write the header on disk
- HEADER_BUFFER.put( HEADER_BYTES );
- HEADER_BUFFER.flip();
+ position = writeData( RECORD_MANAGER_HEADER_BYTES, position, firstFreePage );
+
+ // The offset of the current B-tree of B-trees
+ position = writeData( RECORD_MANAGER_HEADER_BYTES, position, currentBtreeOfBtreesOffset );
+
+ // The offset of the copied pages B-tree
+ position = writeData( RECORD_MANAGER_HEADER_BYTES, position, previousBtreeOfBtreesOffset );
+
+ // The offset of the current B-tree of B-trees
+ position = writeData( RECORD_MANAGER_HEADER_BYTES, position, currentCopiedPagesBtreeOffset );
+
+ // The offset of the copied pages B-tree
+ position = writeData( RECORD_MANAGER_HEADER_BYTES, position, previousCopiedPagesBtreeOffset );
+
+ // Write the RecordManager header on disk
+ RECORD_MANAGER_HEADER_BUFFER.put( RECORD_MANAGER_HEADER_BYTES );
+ RECORD_MANAGER_HEADER_BUFFER.flip();
LOG.debug( "Update RM header, FF : {}", firstFreePage );
- fileChannel.write( HEADER_BUFFER, 0 );
- HEADER_BUFFER.clear();
+
+ try
+ {
+ fileChannel.write( RECORD_MANAGER_HEADER_BUFFER, 0 );
+ }
+ catch ( IOException ioe )
+ {
+ throw new FileException( ioe.getMessage() );
+ }
+
+ RECORD_MANAGER_HEADER_BUFFER.clear();
nbUpdateRMHeader.incrementAndGet();
}
/**
- * Update the BTree header after a BTree modification. This will make the latest modification
- * visible.
+ * Inject an int into a byte[] at a given position.
+ */
+ private int writeData( byte[] buffer, int position, int value )
+ {
+ RECORD_MANAGER_HEADER_BYTES[position] = ( byte ) ( value >>> 24 );
+ RECORD_MANAGER_HEADER_BYTES[position+1] = ( byte ) ( value >>> 16 );
+ RECORD_MANAGER_HEADER_BYTES[position+2] = ( byte ) ( value >>> 8 );
+ RECORD_MANAGER_HEADER_BYTES[position+3] = ( byte ) ( value );
+
+ return position + 4;
+ }
+
+
+ /**
+ * Inject a long into a byte[] at a given position.
+ */
+ private int writeData( byte[] buffer, int position, long value )
+ {
+ RECORD_MANAGER_HEADER_BYTES[position] = ( byte ) ( value >>> 56 );
+ RECORD_MANAGER_HEADER_BYTES[position+1] = ( byte ) ( value >>> 48 );
+ RECORD_MANAGER_HEADER_BYTES[position+2] = ( byte ) ( value >>> 40 );
+ RECORD_MANAGER_HEADER_BYTES[position+3] = ( byte ) ( value >>> 32 );
+ RECORD_MANAGER_HEADER_BYTES[position+4] = ( byte ) ( value >>> 24 );
+ RECORD_MANAGER_HEADER_BYTES[position+5] = ( byte ) ( value >>> 16 );
+ RECORD_MANAGER_HEADER_BYTES[position+6] = ( byte ) ( value >>> 8 );
+ RECORD_MANAGER_HEADER_BYTES[position+7] = ( byte ) ( value );
+
+ return position + 8;
+ }
+
+
+ /**
+ * Add a new <btree, revision> tuple into the B-tree of B-trees.
+ *
+ * @param name The B-tree name
+ * @param revision The B-tree revision
+ * @param btreeHeaderOffset The B-tree offset
+ * @throws IOException If the update failed
+ */
+ /* no qualifier */ <K, V> void addInBtreeOfBtrees( String name, long revision, long btreeHeaderOffset ) throws IOException
+ {
+ checkOffset( btreeHeaderOffset );
+ NameRevision nameRevision = new NameRevision( name, revision );
+
+ btreeOfBtrees.insert( nameRevision, btreeHeaderOffset );
+
+ // Update the B-tree of B-trees
+ currentBtreeOfBtreesOffset = ((PersistedBTree<NameRevision, Long>)btreeOfBtrees).getBtreeOffset();
+ }
+
+
+ /**
+ * Internal method used to update the B-tree of B-trees offset
+ * @param btreeOfBtreesOffset The new offset
+ */
+ /* no qualifier */ void setBtreeOfBtreesOffset( long btreeOfBtreesOffset )
+ {
+ checkOffset( btreeOfBtreesOffset );
+ this.currentBtreeOfBtreesOffset = btreeOfBtreesOffset;
+ }
+
+
+ /**
+ * Write the B-tree header on disk. We will write the following informations :
+ * <pre>
+ * +------------+
+ * | revision | The B-tree revision
+ * +------------+
+ * | nbElems | The B-tree number of elements
+ * +------------+
+ * | rootPage | The root page offset
+ * +------------+
+ * | pageSize | The B-tree page size (ie, the number of elements per page max)
+ * +------------+
+ * | nameSize | The B-tree name size
+ * +------------+
+ * | name | The B-tree name
+ * +------------+
+ * | keySerSize | The keySerializer FQCN size
+ * +------------+
+ * | keySerFQCN | The keySerializer FQCN
+ * +------------+
+ * | valSerSize | The Value serializer FQCN size
+ * +------------+
+ * | valSerKQCN | The valueSerializer FQCN
+ * +------------+
+ * | dups | The flags that tell if the dups are allowed
+ * +------------+
+ * </pre>
+ * @param btree The B-tree which header has to be written
+ * @return The B-tree header offset
+ * @throws IOException If we weren't able to write the B-tree header
+ */
+ private <K, V> long writeBtreeHeader( BTree<K, V> btree ) throws IOException
+ {
+ // We will add the newly managed B-tree at the end of the header.
+ byte[] btreeNameBytes = Strings.getBytesUtf8( btree.getName() );
+ byte[] keySerializerBytes = Strings.getBytesUtf8( btree.getKeySerializerFQCN() );
+ byte[] valueSerializerBytes = Strings.getBytesUtf8( btree.getValueSerializerFQCN() );
+
+ int bufferSize =
+ LONG_SIZE + // The revision
+ LONG_SIZE + // the number of element
+ LONG_SIZE + // The root page offset
+ INT_SIZE + // The page size
+ INT_SIZE + // The name size
+ btreeNameBytes.length + // The name
+ INT_SIZE + // The keySerializerBytes size
+ keySerializerBytes.length + // The keySerializerBytes
+ INT_SIZE + // The valueSerializerBytes size
+ valueSerializerBytes.length + // The valueSerializerBytes
+ INT_SIZE; // The allowDuplicates flag
+
+ // Get the pageIOs we need to store the data. We may need more than one.
+ PageIO[] btreeHeaderPageIos = getFreePageIOs( bufferSize );
+
+ // Store the B-tree header Offset into the B-tree
+ long btreeOffset = btreeHeaderPageIos[0].getOffset();
+ ( ( PersistedBTree<K, V> ) btree ).setBtreeOffset( btreeOffset );
+
+ // Now store the B-tree data in the pages :
+ // - the B-tree revision
+ // - the B-tree number of elements
+ // - the B-tree root page offset
+ // - the B-tree page size
+ // - the B-tree name
+ // - the keySerializer FQCN
+ // - the valueSerializer FQCN
+ // - the flags that tell if the dups are allowed
+ // Starts at 0
+ long position = 0L;
+
+ // The B-tree current revision
+ position = store( position, btree.getRevision(), btreeHeaderPageIos );
+
+ // The nb elems in the tree
+ position = store( position, btree.getNbElems(), btreeHeaderPageIos );
+
+ // Serialize the B-tree root page
+ Page<K, V> rootPage = BTreeFactory.getRootPage( btree );
+
+ PageIO[] rootPageIos = serializePage( btree, btree.getRevision(), rootPage );
+
+ // Get the reference on the first page
+ PageIO rootPageIo = rootPageIos[0];
+
+ // Now, we can inject the B-tree rootPage offset into the B-tree header
+ position = store( position, rootPageIo.getOffset(), btreeHeaderPageIos );
+ ( ( PersistedBTree<K, V> ) btree ).setRootPageOffset( rootPageIo.getOffset() );
+ ( ( PersistedLeaf<K, V> ) rootPage ).setOffset( rootPageIo.getOffset() );
+
+ // The B-tree page size
+ position = store( position, btree.getPageSize(), btreeHeaderPageIos );
+
+ // The tree name
+ position = store( position, btreeNameBytes, btreeHeaderPageIos );
+
+ // The keySerializer FQCN
+ position = store( position, keySerializerBytes, btreeHeaderPageIos );
+
+ // The valueSerialier FQCN
+ position = store( position, valueSerializerBytes, btreeHeaderPageIos );
+
+ // The allowDuplicates flag
+ position = store( position, ( btree.isAllowDuplicates() ? 1 : 0 ), btreeHeaderPageIos );
+
+ // And flush the pages to disk now
+ LOG.debug( "Flushing the newly managed '{}' btree header", btree.getName() );
+ flushPages( btreeHeaderPageIos );
+
+ LOG.debug( "Flushing the newly managed '{}' btree rootpage", btree.getName() );
+ flushPages( rootPageIos );
+
+ return btreeOffset;
+ }
+
+
+ /**
+ * Update the B-tree header after a B-tree modification. This will make the latest modification
+ * visible.<br/>
+ * We update the following fields :
+ * <ul>
+ * <li>the revision</li>
+ * <li>the number of elements</li>
+ * <li>the B-tree root page offset</li>
+ * </ul>
+ * <br/>
+ * As a result, a new version of the BtreHeader will be created, which will replace the previous
+ * B-tree header
+ * @param btree TheB-treeto update
+ * @param rootPageOffset The offset of the modified rootPage
+ * @return The offset of the new B-tree Header
+ * @throws IOException If we weren't able to write the file on disk
+ * @throws EndOfFileExceededException If we tried to write after the end of the file
+ */
+ /* no qualifier */ <K, V> long updateBtreeHeader( BTree<K, V> btree, long rootPageOffset )
+ throws EndOfFileExceededException, IOException
+ {
+ return updateBtreeHeader( btree, rootPageOffset, false );
+ }
+
+
+ /**
+ * Update the B-tree header after a B-tree modification. This will make the latest modification
+ * visible.<br/>
+ * We update the following fields :
+ * <ul>
+ * <li>the revision</li>
+ * <li>the number of elements</li>
+ * <li>the reference to the current B-tree revisions</li>
+ * <li>the reference to the old B-tree revisions</li>
+ * </ul>
+ * <br/>
+ * As a result, we new version of the BtreHeader will be created
+ * @param btree The B-tree to update
+ * @param rootPageOffset The offset of the modified rootPage
+ * @return The offset of the new B-tree Header if it has changed (ie, when the onPlace flag is set to true)
+ * @throws IOException
+ * @throws EndOfFileExceededException
+ */
+ /* no qualifier */ <K, V> void updateBtreeHeaderOnPlace( BTree<K, V> btree, long rootPageOffset )
+ throws EndOfFileExceededException,
+ IOException
+ {
+ updateBtreeHeader( btree, rootPageOffset, true );
+ }
+
+
+ /**
+ * Update the B-tree header after a B-tree modification. This will make the latest modification
+ * visible.<br/>
* We update the following fields :
* <ul>
* <li>the revision</li>
* <li>the number of elements</li>
- * <li>the reference to the current BTree revisions</li>
- * <li>the reference to the old BTree revisions</li>
+ * <li>the reference to the current B-tree revisions</li>
+ * <li>the reference to the old B-tree revisions</li>
* </ul>
- * @param btree
- * @throws IOException
- * @throws EndOfFileExceededException
+ * <br/>
+ * As a result, a new version of the BtreHeader will be created, which may replace the previous
+ * B-tree header (if the onPlace flag is set to true) or a new set of pageIos will contain the new
+ * version.
+ *
+ * @param btree The B-tree to update
+ * @param rootPageOffset The offset of the modified rootPage
+ * @param onPlace Tells if we modify the B-tree on place, or if we create a copy
+ * @return The offset of the new B-tree Header if it has changed (ie, when the onPlace flag is set to true)
+ * @throws EndOfFileExceededException If we tried to write after the end of the file
+ * @throws IOException If tehre were some error while writing the data on disk
*/
- /* No qualifier*/<K, V> void updateBtreeHeader( BTree<K, V> btree, long rootPageOffset )
- throws EndOfFileExceededException,
- IOException
+ private <K, V> long updateBtreeHeader( BTree<K, V> btree, long rootPageOffset, boolean onPlace )
+ throws EndOfFileExceededException, IOException
{
- // Read the pageIOs associated with this BTree
+ // Read the pageIOs associated with this B-tree
+ PageIO[] pageIos;
+ long newBtreeHeaderOffset = NO_PAGE;
long offset = ( ( PersistedBTree<K, V> ) btree ).getBtreeOffset();
- long headerSize = LONG_SIZE + LONG_SIZE + LONG_SIZE;
- PageIO[] pageIos = readPageIOs( offset, headerSize );
+ if ( onPlace )
+ {
+ // We just have to update the existing BTreeHeader
+ long headerSize = LONG_SIZE + LONG_SIZE + LONG_SIZE;
- // Now, update the revision
- long position = 0;
+ pageIos = readPageIOs( offset, headerSize );
- position = store( position, btree.getRevision(), pageIos );
- position = store( position, btree.getNbElems(), pageIos );
- position = store( position, rootPageOffset, pageIos );
+ // Now, update the revision
+ long position = 0;
- // Write the pages on disk
- if ( LOG.isDebugEnabled() )
- {
- LOG.debug( "-----> Flushing the '{}' BTreeHeader", btree.getName() );
- LOG.debug( " revision : " + btree.getRevision() + ", NbElems : " + btree.getNbElems() + ", root offset : "
- + rootPageOffset );
+ position = store( position, btree.getRevision(), pageIos );
+ position = store( position, btree.getNbElems(), pageIos );
+ position = store( position, rootPageOffset, pageIos );
+
+ // Write the pages on disk
+ if ( LOG.isDebugEnabled() )
+ {
+ LOG.debug( "-----> Flushing the '{}' B-treeHeader", btree.getName() );
+ LOG.debug( " revision : " + btree.getRevision() + ", NbElems : " + btree.getNbElems() + ", root offset : "
+ + rootPageOffset );
+ }
+
+ // Get new place on disk to store the modified BTreeHeader if it's not onPlace
+ // Rewrite the pages at the same place
+ LOG.debug( "Rewriting the B-treeHeader on place for B-tree " + btree.getName() );
+ flushPages( pageIos );
}
+ else
+ {
+ // We have to read and copy the existing BTreeHeader and to create a new one
+ pageIos = readPageIOs( offset, Long.MAX_VALUE );
- flushPages( pageIos );
+ // Now, copy every read page
+ PageIO[] newPageIOs = new PageIO[pageIos.length];
+ int pos = 0;
+
+ for ( PageIO pageIo : pageIos )
+ {
+ // Fetch a free page
+ newPageIOs[pos] = fetchNewPage();
+
+ // keep a track of the allocated and copied pages so that we can
+ // free them when we do a commit or rollback, if the btree is an management one
+ if ( btree.getType() == BTreeTypeEnum.PERSISTED_MANAGEMENT )
+ {
+ freedPages.add( pageIo );
+ allocatedPages.add( newPageIOs[pos] );
+ }
+
+ pageIo.copy( newPageIOs[pos] );
+
+ if ( pos > 0 )
+ {
+ newPageIOs[pos - 1].setNextPage( newPageIOs[pos].getOffset() );
+ }
- nbUpdateBTreeHeader.incrementAndGet();
+ pos++;
+ }
+
+ // store the new rootPage offset
+ // Now, update the revision
+ long position = 0;
+
+ position = store( position, btree.getRevision(), newPageIOs );
+ position = store( position, btree.getNbElems(), newPageIOs );
+ position = store( position, rootPageOffset, newPageIOs );
+
+ // Get new place on disk to store the modified BTreeHeader if it's not onPlace
+ // Flush the new B-treeHeader on disk
+ LOG.debug( "Rewriting the B-treeHeader on place for B-tree " + btree.getName() );
+ flushPages( newPageIOs );
+
+ newBtreeHeaderOffset = newPageIOs[0].getOffset();
+ }
+
+ nbUpdateBtreeHeader.incrementAndGet();
if ( LOG_CHECK.isDebugEnabled() )
{
check();
}
+
+ return newBtreeHeaderOffset;
}
/**
- * Write the pages in the disk, either at the end of the file, or at
+ * Write the pages on disk, either at the end of the file, or at
* the position they were taken from.
*
* @param pageIos The list of pages to write
@@ -1606,6 +2017,7 @@ public class RecordManager
for ( PageIO pageIo : pageIos )
{
+ ByteBuffer data = pageIo.getData();
pageIo.getData().rewind();
if ( fileChannel.size() < ( pageIo.getOffset() + pageSize ) )
@@ -1660,7 +2072,7 @@ public class RecordManager
* @param position The position in a virtual byte[] if all the pages were contiguous
* @param bytes The byte[] to serialize
* @param pageIos The pageIOs we have to store the data in
- * @return The new position
+ * @return The new offset
*/
private long store( long position, byte[] bytes, PageIO... pageIos )
{
@@ -1729,7 +2141,7 @@ public class RecordManager
* @param position The position in a virtual byte[] if all the pages were contiguous
* @param bytes The byte[] to serialize
* @param pageIos The pageIOs we have to store the data in
- * @return The new position
+ * @return The new offset
*/
private long storeRaw( long position, byte[] bytes, PageIO... pageIos )
{
@@ -1801,7 +2213,7 @@ public class RecordManager
* @param position The position in a virtual byte[] if all the pages were contiguous
* @param value The int to serialize
* @param pageIos The pageIOs we have to store the data in
- * @return The new position
+ * @return The new offset
*/
private long store( long position, int value, PageIO... pageIos )
{
@@ -1876,7 +2288,7 @@ public class RecordManager
* @param position The position in a virtual byte[] if all the pages were contiguous
* @param value The long to serialize
* @param pageIos The pageIOs we have to store the data in
- * @return The new position
+ * @return The new offset
*/
private long store( long position, long value, PageIO... pageIos )
{
@@ -1977,10 +2389,9 @@ public class RecordManager
/**
- * Stores a new page on disk. We will add the modified page into the tree of copied pages.
- * The new page is serialized and saved on disk.
+ * Write the page in a serialized form.
*
- * @param btree The persistedBTree we will create a new PageHolder for
+ * @param btree The persistedBtree we will create a new PageHolder for
* @param newPage The page to write on disk
* @param newRevision The page's revision
* @return A PageHolder containing the copied page
@@ -1992,6 +2403,7 @@ public class RecordManager
// We first need to save the new page on disk
PageIO[] pageIos = serializePage( btree, newRevision, newPage );
+// System.out.println( "Write data for '" + btree.getName() + "' btree " );
LOG.debug( "Write data for '{}' btree ", btree.getName() );
// Write the page on disk
@@ -2053,7 +2465,8 @@ public class RecordManager
/**
- * Get as many pages as needed to store the data of the given size
+ * Get as many pages as needed to store the data of the given size. The returned
+ * PageIOs are all linked together.
*
* @param dataSize The data size
* @return An array of pages, enough to store the full data
@@ -2094,6 +2507,8 @@ public class RecordManager
*/
private PageIO fetchNewPage() throws IOException
{
+ dumpFreePages( firstFreePage );
+
if ( firstFreePage == NO_PAGE )
{
nbCreatedPages.incrementAndGet();
@@ -2146,6 +2561,8 @@ public class RecordManager
*/
private PageIO fetchPage( long offset ) throws IOException, EndOfFileExceededException
{
+ checkOffset( offset );
+
if ( fileChannel.size() < offset + pageSize )
{
// Error : we are past the end of the file
@@ -2178,14 +2595,20 @@ public class RecordManager
}
- public void setPageSize( int pageSize )
+ /**
+ * Set the page size, ie the number of bytes a page can store.
+ *
+ * @param pageSize The number of bytes for a page
+ */
+ /* no qualifier */ void setPageSize( int pageSize )
{
- if ( this.pageSize != -1 )
+ if ( this.pageSize >= 13 )
{
+ this.pageSize = pageSize;
}
else
{
- this.pageSize = pageSize;
+ this.pageSize = DEFAULT_PAGE_SIZE;
}
}
@@ -2195,14 +2618,26 @@ public class RecordManager
*/
public void close() throws IOException
{
- // TODO : we must wait for the last write to finish
+ if ( isTransactionStarted() )
+ {
+ // Wait for the transaction to finish
+ }
+ else
+ {
+ beginTransaction();
+ }
- for ( BTree<Object, Object> tree : managedBTrees.values() )
+ // Close all the managed B-trees
+ for ( BTree<Object, Object> tree : managedBtrees.values() )
{
tree.close();
}
- managedBTrees.clear();
+ // Close the management B-trees
+ copiedPageBtree.close();
+ btreeOfBtrees.close();
+
+ managedBtrees.clear();
// Write the data
fileChannel.force( true );
@@ -2211,6 +2646,7 @@ public class RecordManager
fileChannel.close();
}
+
/** Hex chars */
private static final byte[] HEX_CHAR = new byte[]
{ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
@@ -2248,8 +2684,6 @@ public class RecordManager
System.out.println( " 0 1 2 3 4 5 6 7 8 9 A B C D E F " );
System.out.println( "+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+" );
- int position = buffer.position();
-
for ( int i = 0; i < buffer.limit(); i += 16 )
{
System.out.print( "|" );
@@ -2279,87 +2713,289 @@ public class RecordManager
* Dump the RecordManager file
* @throws IOException
*/
- public void dump() throws IOException
+ public void dump()
{
- RandomAccessFile randomFile = new RandomAccessFile( file, "r" );
- FileChannel fileChannel = randomFile.getChannel();
+ LOG.debug( "/---------------------------- Dump ----------------------------\\" );
+// System.out.println( "/---------------------------- Dump ----------------------------\\" );
+
+ try
+ {
+ RandomAccessFile randomFile = new RandomAccessFile( file, "r" );
+ FileChannel fileChannel = randomFile.getChannel();
- ByteBuffer header = ByteBuffer.allocate( HEADER_SIZE );
+ ByteBuffer recordManagerHeader = ByteBuffer.allocate( RECORD_MANAGER_HEADER_SIZE );
- // load the header
- fileChannel.read( header );
+ // load the RecordManager header
+ fileChannel.read( recordManagerHeader );
- header.rewind();
+ recordManagerHeader.rewind();
- // The page size
- int pageSize = header.getInt();
+ // The page size
+ long fileSize = fileChannel.size();
+ int pageSize = recordManagerHeader.getInt();
+ long nbPages = fileSize / pageSize;
- // The number of managed BTrees
- int nbBTree = header.getInt();
+ // The number of managed B-trees
+ int nbBtree = recordManagerHeader.getInt();
- // The first and last free page
- long firstFreePage = header.getLong();
+ // The first free page
+ long firstFreePage = recordManagerHeader.getLong();
- if ( LOG.isDebugEnabled() )
+ // The current B-tree of B-trees
+ long currentBtreeOfBtreesPage = recordManagerHeader.getLong();
+
+ // The previous B-tree of B-trees
+ long previousBtreeOfBtreesPage = recordManagerHeader.getLong();
+
+ // The current CopiedPages B-tree
+ long currentCopiedPagesBtreePage = recordManagerHeader.getLong();
+
+ // The previous CopiedPages B-tree
+ long previousCopiedPagesBtreePage = recordManagerHeader.getLong();
+
+ if ( LOG.isDebugEnabled() )
+ {
+ LOG.debug( " RecordManager" );
+ LOG.debug( " -------------" );
+ LOG.debug( " Header " );
+ LOG.debug( " Size = 0x{}", Long.toHexString( fileSize ) );
+ LOG.debug( " NbPages = {}", nbPages );
+ LOG.debug( " page size : {}", pageSize );
+ LOG.debug( " nbTree : {}", nbBtree );
+ LOG.debug( " firstFreePage : {}", Long.toHexString( firstFreePage ) );
+ LOG.debug( " current BOB : {}", Long.toHexString( currentBtreeOfBtreesPage ) );
+ LOG.debug( " previous BOB : {}", Long.toHexString( previousBtreeOfBtreesPage ) );
+ LOG.debug( " current CopiedPages : {}", Long.toHexString( currentCopiedPagesBtreePage ) );
+ LOG.debug( " previous CopiedPages : {}", Long.toHexString( previousCopiedPagesBtreePage ) );
+ }
+
+// System.out.println( " RecordManager" );
+// System.out.println( " -------------" );
+// System.out.println( " Size = 0x" + Long.toHexString( fileSize ) );
+// System.out.println( " NbPages = " + nbPages );
+// System.out.println( " Header " );
+// System.out.println( " page size : " + pageSize );
+// System.out.println( " nbTree : " + nbBtree );
+// System.out.println( " firstFreePage : 0x" + Long.toHexString( firstFreePage ) );
+// System.out.println( " current BOB : 0x" + Long.toHexString( currentBtreeOfBtreesPage ) );
+// System.out.println( " previous BOB : 0x" + Long.toHexString( previousBtreeOfBtreesPage ) );
+// System.out.println( " current CopiedPages : 0x" + Long.toHexString( currentCopiedPagesBtreePage ) );
+// System.out.println( " previous CopiedPages : 0x" + Long.toHexString( previousCopiedPagesBtreePage ) );
+
+ long position = RECORD_MANAGER_HEADER_SIZE;
+
+ // Dump the Free pages list
+ dumpFreePages( firstFreePage );
+
+ // Dump the B-tree of B-trees
+ dumpBtreeHeader( currentBtreeOfBtreesPage );
+
+ // Dump the previous B-tree of B-trees if any
+ if ( previousBtreeOfBtreesPage != NO_PAGE )
+ {
+ dumpBtreeHeader( previousBtreeOfBtreesPage );
+ }
+
+ // Dump the CopiedPages B-tree
+ dumpBtreeHeader( currentCopiedPagesBtreePage );
+
+
+ // Dump the previous B-tree of B-trees if any
+ if ( previousCopiedPagesBtreePage != NO_PAGE )
+ {
+ dumpBtreeHeader( previousCopiedPagesBtreePage );
+ }
+
+ // Dump all the user's B-tree
+ randomFile.close();
+ LOG.debug( "\\---------------------------- Dump ----------------------------/" );
+// System.out.println( "\\---------------------------- Dump ----------------------------/" );
+ }
+ catch ( IOException ioe )
{
- LOG.debug( "RecordManager" );
- LOG.debug( "-------------" );
- LOG.debug( " Header " );
- LOG.debug( " '{}'", Strings.dumpBytes( header.array() ) );
- LOG.debug( " page size : {}", pageSize );
- LOG.debug( " nbTree : {}", nbBTree );
- LOG.debug( " firstFreePage : {}", firstFreePage );
+ System.out.println( "Exception while dumping the file : " + ioe.getMessage() );
}
+ }
- long position = HEADER_SIZE;
- // Dump the BTrees
- for ( int i = 0; i < nbBTree; i++ )
+ /**
+ * Dump the free pages
+ */
+ private void dumpFreePages( long freePageOffset ) throws EndOfFileExceededException, IOException
+ {
+// System.out.println( "\n FreePages : " );
+ LOG.debug( "\n FreePages : " );
+ int pageNb = 1;
+
+ while ( freePageOffset != NO_PAGE )
{
- LOG.debug( " Btree[{}]", i );
- PageIO[] pageIos = readPageIOs( position, Long.MAX_VALUE );
+ PageIO pageIo = fetchPage( freePageOffset );
+
+ LOG.debug( " freePage[{}] : 0x{}", pageNb, Long.toHexString( pageIo.getOffset() ) );
+// System.out.println( " freePage[" + pageNb + "] : 0x" + Long.toHexString( pageIo.getOffset() ) );
+
+ freePageOffset = pageIo.getNextPage();
+ pageNb++;
+ }
+ }
+
+
+ /**
+ * Dump a B-tree Header
+ */
+ private long dumpBtreeHeader( long btreeOffset ) throws EndOfFileExceededException, IOException
+ {
+ // First read the B-tree header
+ PageIO[] pageIos = readPageIOs( btreeOffset, Long.MAX_VALUE );
+
+ long dataPos = 0L;
+
+ // The B-tree current revision
+ long revision = readLong( pageIos, dataPos );
+ dataPos += LONG_SIZE;
+
+ // The nb elems in the tree
+ long nbElems = readLong( pageIos, dataPos );
+ dataPos += LONG_SIZE;
+
+ // The B-tree rootPage offset
+ long rootPageOffset = readLong( pageIos, dataPos );
+ dataPos += LONG_SIZE;
+
+ // The B-tree page size
+ int btreePageSize = readInt( pageIos, dataPos );
+ dataPos += INT_SIZE;
+
+ // The tree name
+ ByteBuffer btreeNameBytes = readBytes( pageIos, dataPos );
+ dataPos += INT_SIZE + btreeNameBytes.limit();
+ String btreeName = Strings.utf8ToString( btreeNameBytes );
+
+ // The keySerializer FQCN
+ ByteBuffer keySerializerBytes = readBytes( pageIos, dataPos );
+ dataPos += INT_SIZE + keySerializerBytes.limit();
+
+ String keySerializerFqcn = "";
+
+ if ( keySerializerBytes != null )
+ {
+ keySerializerFqcn = Strings.utf8ToString( keySerializerBytes );
+ }
+
+ // The valueSerialier FQCN
+ ByteBuffer valueSerializerBytes = readBytes( pageIos, dataPos );
+
+ String valueSerializerFqcn = "";
+ dataPos += INT_SIZE + valueSerializerBytes.limit();
+
+ if ( valueSerializerBytes != null )
+ {
+ valueSerializerFqcn = Strings.utf8ToString( valueSerializerBytes );
+ }
+
+ // The B-tree allowDuplicates flag
+ int allowDuplicates = readInt( pageIos, dataPos );
+ boolean dupsAllowed = allowDuplicates != 0;
+
+ dataPos += INT_SIZE;
+
+// System.out.println( "\n B-Tree " + btreeName );
+// System.out.println( " ------------------------- " );
+
+// System.out.println( " nbPageIOs[" + pageIos.length + "] = " + pageIoList );
+ if ( LOG.isDebugEnabled() )
+ {
+ StringBuilder sb = new StringBuilder();
+ boolean isFirst = true;
for ( PageIO pageIo : pageIos )
{
- LOG.debug( " {}", pageIo );
+ if ( isFirst )
+ {
+ isFirst = false;
+ }
+ else
+ {
+ sb.append( ", " );
+ }
+
+ sb.append( "0x" ).append( Long.toHexString( pageIo.getOffset() ) );
}
+
+ String pageIoList = sb.toString();
+
+ LOG.debug( " PageIOs[{}] = {}", pageIos.length, pageIoList );
+
+// System.out.println( " dataSize = "+ pageIos[0].getSize() );
+ LOG.debug( " dataSize = {}", pageIos[0].getSize() );
+
+ LOG.debug( " B-tree '{}'", btreeName );
+ LOG.debug( " revision : {}", revision );
+ LOG.debug( " nbElems : {}", nbElems );
+ LOG.debug( " rootPageOffset : 0x{}", Long.toHexString( rootPageOffset ) );
+ LOG.debug( " B-tree page size : {}", btreePageSize );
+ LOG.debug( " keySerializer : '{}'", keySerializerFqcn );
+ LOG.debug( " valueSerializer : '{}'", valueSerializerFqcn );
+ LOG.debug( " dups allowed : {}", dupsAllowed );
+//
+// System.out.println( " B-tree '" + btreeName + "'" );
+// System.out.println( " revision : " + revision );
+// System.out.println( " nbElems : " + nbElems );
+// System.out.println( " rootPageOffset : 0x" + Long.toHexString( rootPageOffset ) );
+// System.out.println( " B-tree page size : " + btreePageSize );
+// System.out.println( " keySerializer : " + keySerializerFqcn );
+// System.out.println( " valueSerializer : " + valueSerializerFqcn );
+// System.out.println( " dups allowed : " + dupsAllowed );
}
- randomFile.close();
+ return rootPageOffset;
}
/**
- * Get the number of managed trees. We don't count the CopiedPage BTree. and the Revsion BTree
+ * Get the number of managed trees. We don't count the CopiedPage B-tree and the B-tree of B-trees
*
- * @return The number of managed BTrees
+ * @return The number of managed B-trees
*/
public int getNbManagedTrees()
{
- return nbBtree - 2;
+ return nbBtree;
}
/**
- * Get the managed trees. We don't return the CopiedPage BTree nor the Revision BTree.
+ * Get the managed B-trees. We don't return the CopiedPage B-tree nor the B-tree of B-trees.
*
- * @return The managed BTrees
+ * @return The managed B-trees
*/
public Set<String> getManagedTrees()
{
- Set<String> btrees = new HashSet<String>( managedBTrees.keySet() );
-
- btrees.remove( COPIED_PAGE_BTREE_NAME );
- btrees.remove( REVISION_BTREE_NAME );
+ Set<String> btrees = new HashSet<String>( managedBtrees.keySet() );
return btrees;
}
/**
- * Store a reference to an old rootPage into the Revision BTree
+ * Stores the copied pages into the CopiedPages B-tree
+ *
+ * @param name The B-tree name
+ * @param revision The revision
+ * @param copiedPages The pages that have been copied while creating this revision
+ * @throws IOException If we weren't able to store the data on disk
+ */
+ /* No Qualifier */ void storeCopiedPages( String name, long revision, long[] copiedPages ) throws IOException
+ {
+ RevisionName revisionName = new RevisionName( revision, name );
+
+ copiedPageBtree.insert( revisionName, copiedPages );
+ }
+
+
+ /**
+ * Store a reference to an old rootPage into the Revision B-tree
*
- * @param btree The BTree we want to keep an old RootPage for
+ * @param btree The B-tree we want to keep an old RootPage for
* @param rootPage The old rootPage
* @throws IOException If we have an issue while writing on disk
*/
@@ -2370,14 +3006,14 @@ public class RecordManager
return;
}
- if ( ( btree == copiedPageBTree ) || ( btree == revisionBTree ) )
+ if ( btree == copiedPageBtree )
{
return;
}
- RevisionName revisionName = new RevisionName( rootPage.getRevision(), btree.getName() );
+ NameRevision nameRevision = new NameRevision( btree.getName(), rootPage.getRevision() );
- ( ( AbstractBTree<RevisionName, Long> ) revisionBTree ).insert( revisionName,
+ ( ( AbstractBTree<NameRevision, Long> ) btreeOfBtrees ).insert( nameRevision,
( ( AbstractPage<K, V> ) rootPage ).getOffset(), 0 );
if ( LOG_CHECK.isDebugEnabled() )
@@ -2388,12 +3024,12 @@ public class RecordManager
/**
- * Fetch the rootPage of a given BTree for a given revision.
+ * Fetch the rootPage of a given B-tree for a given revision.
*
- * @param btree The BTree we are interested in
+ * @param btree The B-tree we are interested in
* @param revision The revision we want to get back
- * @return The rootPage for this BTree and this revision, if any
- * @throws KeyNotFoundException If we can't find the rootPage for this revision and this BTree
+ * @return The rootPage for this B-tree and this revision, if any
+ * @throws KeyNotFoundException If we can't find the rootPage for this revision and this B-tree
* @throws IOException If we had an ise while accessing the data on disk
*/
/* No qualifier */<K, V> Page<K, V> getRootPage( BTree<K, V> btree, long revision ) throws KeyNotFoundException,
@@ -2405,12 +3041,33 @@ public class RecordManager
return btree.getRootPage();
}
- RevisionName revisionName = new RevisionName( revision, btree.getName() );
- long rootPageOffset = revisionBTree.get( revisionName );
+ // Get the B-tree header offset
+ NameRevision nameRevision = new NameRevision( btree.getName(), revision );
+ long btreeHeaderOffset = btreeOfBtrees.get( nameRevision );
+
+ // get the B-tree rootPage
+ Page<K, V> btreeRoot = readRootPage( btree, btreeHeaderOffset );
+
+ return btreeRoot;
+ }
+
+
+ /**
+ * Read a root page from the B-tree header offset
+ */
+ private <K, V> Page<K, V> readRootPage( BTree<K, V> btree, long btreeHeaderOffset ) throws EndOfFileExceededException, IOException
+ {
+ // Read the B-tree header pages on disk
+ PageIO[] btreeHeaderPageIos = readPageIOs( btreeHeaderOffset, Long.MAX_VALUE );
+ long dataPos = LONG_SIZE + LONG_SIZE;
+
+ // The B-tree rootPage offset
+ long rootPageOffset = readLong( btreeHeaderPageIos, dataPos );
// Read the rootPage pages on disk
PageIO[] rootPageIos = readPageIOs( rootPageOffset, Long.MAX_VALUE );
+ // Now, convert it to a Page
Page<K, V> btreeRoot = readPage( btree, rootPageIos );
return btreeRoot;
@@ -2420,34 +3077,30 @@ public class RecordManager
/**
* Get one managed trees, knowing its name.
*
- * @return The managed BTrees
+ * @param name The B-tree name we are looking for
+ * @return The managed B-trees
*/
public <K, V> BTree<K, V> getManagedTree( String name )
{
- return ( BTree<K, V> ) managedBTrees.get( name );
+ return ( BTree<K, V> ) managedBtrees.get( name );
}
/**
- * Move a list of pages to the free page list. A logical page is associated with on
- * or physical PageIO, which are on the disk. We have to move all those PagIO instance
+ * Move a list of pages to the free page list. A logical page is associated with one
+ * or more physical PageIOs, which are on the disk. We have to move all those PagIO instances
* to the free list, and do the same in memory (we try to keep a reference to a set of
* free pages.
*
- * @param btree The BTree which were owning the pages
+ * @param btree The B-tree which were owning the pages
+ * @param revision The current revision
[... 550 lines stripped ...]