* +--------------------+-------------+ * | revision | 8 bytes | @@ -49,40 +50,26 @@ import java.util.concurrent.atomic.Atomi * * @author Apache Directory Project */ -/* No qualifier*/class BTreeHeader +/* No qualifier*/class BTreeHeader{ /** The current revision */ - private AtomicLong revision = new AtomicLong( 0L ); + private long revision = 0L; - /** The number of elements in this BTree */ - private AtomicLong nbElems = new AtomicLong( 0L ); + /** The number of elements in this B-tree */ + private Long nbElems = 0L; - /** The offset of the BTree RootPage */ + /** The offset of the B-tree RootPage */ private long rootPageOffset; - /** The offset of the next BTree */ - private long nextBTreeOffset; - - /** The number of elements in a page for this BTree */ - private int pageSize; - - /** The BTree name */ - private String name; - - /** The FQCN of the Key serializer */ - private String keySerializerFQCN; - - /** The FQCN of the Value serializer */ - private String valueSerializerFQCN; - // Those are data which aren't serialized : they are in memory only */ /** The position in the file */ private long btreeOffset; - /** The existing versions */ - private long[] versions; + /** A Map containing the rootPage for this tree */ + private Page rootPage; - private int allowDuplicates = 0; + /** The number of users for this BtreeHeader */ + private AtomicInteger nbUsers = new AtomicInteger( 0 ); /** @@ -94,42 +81,6 @@ import java.util.concurrent.atomic.Atomi /** - * @return the name - */ - public String getName() - { - return name; - } - - - /** - * @param name the name to set - */ - /* no qualifier */void setName( String name ) - { - this.name = name; - } - - - /** - * @return the versions - */ - public long[] getVersions() - { - return versions; - } - - - /** - * @param versions the versions to set - */ - /* no qualifier */void setVersions( long[] versions ) - { - this.versions = versions; - } - - - /** * @return the btreeOffset */ public long getBTreeOffset() @@ -170,7 +121,7 @@ import java.util.concurrent.atomic.Atomi */ public long getRevision() { - return revision.get(); + return revision; } @@ -179,18 +130,7 @@ import java.util.concurrent.atomic.Atomi */ /* no qualifier */void setRevision( long revision ) { - this.revision.set( revision ); - } - - - /** - * Increment the revision - * - * @return the new revision - */ - /* no qualifier */long incrementRevision() - { - return revision.incrementAndGet(); + this.revision = revision; } @@ -199,25 +139,7 @@ import java.util.concurrent.atomic.Atomi */ public long getNbElems() { - return nbElems.get(); - } - - - /** - * Increment the number of elements - */ - /* no qualifier */void incrementNbElems() - { - nbElems.incrementAndGet(); - } - - - /** - * Decrement the number of elements - */ - public void decrementNbElems() - { - nbElems.decrementAndGet(); + return nbElems; } @@ -226,91 +148,70 @@ import java.util.concurrent.atomic.Atomi */ /* no qualifier */void setNbElems( long nbElems ) { - this.nbElems.set( nbElems ); + this.nbElems = nbElems; } /** - * @return the nextBTreeOffset - */ - public long getNextBTreeOffset() - { - return nextBTreeOffset; - } - - - /** - * @param nextBtreeOffset the nextBtreeOffset to set + * Increment the number of elements */ - /* no qualifier */void setNextBTreeOffset( long nextBTreeOffset ) + /* no qualifier */void incrementNbElems() { - this.nextBTreeOffset = nextBTreeOffset; + nbElems++; } /** - * @return the pageSize + * Decrement the number of elements */ - public int getPageSize() + /* no qualifier */void decrementNbElems() { - return pageSize; + nbElems--; } /** - * @param pageSize the pageSize to set + * @return the rootPage */ - /* no qualifier */void setPageSize( int pageSize ) + /* no qualifier */ Page getRootPage() { - this.pageSize = pageSize; + return rootPage; } /** - * @return the keySerializerFQCN + * @param rootPage the rootPage to set */ - public String getKeySerializerFQCN() + /* no qualifier */ void setRootPage( Page rootPage ) { - return keySerializerFQCN; + this.rootPage = rootPage; } /** - * @param keySerializerFQCN the keySerializerFQCN to set + * @return the nbUsers */ - /* no qualifier */void setKeySerializerFQCN( String keySerializerFQCN ) + /* no qualifier */ int getNbUsers() { - this.keySerializerFQCN = keySerializerFQCN; + return nbUsers.get(); } /** - * @return the valueSerializerFQCN + * Increment the number of users */ - public String getValueSerializerFQCN() + /* no qualifier */ void incrementNbUsers() { - return valueSerializerFQCN; + nbUsers.incrementAndGet(); } /** - * @param valueSerializerFQCN the valueSerializerFQCN to set + * Decrement the number of users */ - /* no qualifier */void setValueSerializerFQCN( String valueSerializerFQCN ) + /* no qualifier */ void decrementNbUsers() { - this.valueSerializerFQCN = valueSerializerFQCN; - } - - - public boolean isAllowDuplicates() - { - return ( allowDuplicates == 1 ); - } - - - /* no qualifier */void setAllowDuplicates( boolean allowDuplicates ) - { - this.allowDuplicates = ( allowDuplicates ? 1 : 0 ); + nbUsers.decrementAndGet(); } @@ -321,42 +222,12 @@ import java.util.concurrent.atomic.Atomi { StringBuilder sb = new StringBuilder(); - sb.append( "Btree '" ).append( name ).append( "'" ); + sb.append( "B-treeHeader " ); sb.append( ", revision[" ).append( revision ).append( "]" ); sb.append( ", btreeOffset[" ).append( btreeOffset ).append( "]" ); sb.append( ", rootPageOffset[" ).append( rootPageOffset ).append( "]" ); - sb.append( ", nextBTree[" ).append( nextBTreeOffset ).append( "]" ); sb.append( ", nbElems[" ).append( nbElems ).append( "]" ); - sb.append( ", pageSize[" ).append( pageSize ).append( "]" ); - sb.append( ", hasDuplicates[" ).append( isAllowDuplicates() ).append( "]" ); - sb.append( "{\n" ); - sb.append( " Key serializer : " ).append( keySerializerFQCN ).append( "\n" ); - sb.append( " Value serializer : " ).append( valueSerializerFQCN ).append( "\n" ); - sb.append( "}\n" ); - - if ( ( versions != null ) && ( versions.length != 0 ) ) - { - sb.append( "Versions : \n" ); - sb.append( "{\n" ); - - boolean isFirst = true; - - for ( long version : versions ) - { - if ( isFirst ) - { - isFirst = false; - } - else - { - sb.append( ",\n" ); - } - - sb.append( " " ).append( version ); - } - - sb.append( "}\n" ); - } + sb.append( ", nbUsers[" ).append( nbUsers.get() ).append( "]" ); return sb.toString(); } Modified: directory/mavibot/branches/with-txns/mavibot/src/main/java/org/apache/directory/mavibot/btree/BTreeTypeEnum.java URL: http://svn.apache.org/viewvc/directory/mavibot/branches/with-txns/mavibot/src/main/java/org/apache/directory/mavibot/btree/BTreeTypeEnum.java?rev=1566659&r1=1566658&r2=1566659&view=diff ============================================================================== --- directory/mavibot/branches/with-txns/mavibot/src/main/java/org/apache/directory/mavibot/btree/BTreeTypeEnum.java (original) +++ directory/mavibot/branches/with-txns/mavibot/src/main/java/org/apache/directory/mavibot/btree/BTreeTypeEnum.java Mon Feb 10 15:35:18 2014 @@ -21,24 +21,32 @@ package org.apache.directory.mavibot.btr /** - * An enum to describe the BTree type. We have three possible type : + * An enum to describe the B-tree type. We have three possible type : * - *
- * + * * @author Apache Directory Project */ public enum BTreeTypeEnum { - /** Pure in-memory BTree, not persisted on disk */ + /** Pure in-memory B-tree, not persisted on disk */ IN_MEMORY, - /** Persisted BTree */ + /** Persisted B-tree */ PERSISTED, - /** In-memory BTree but saved on disk */ + /** Persisted sub B-tree */ + PERSISTED_SUB, + + /** Persisted Management B-tree */ + PERSISTED_MANAGEMENT, + + /** In-memory B-tree but saved on disk */ BACKED_ON_DISK } Modified: directory/mavibot/branches/with-txns/mavibot/src/main/java/org/apache/directory/mavibot/btree/InMemoryBTree.java URL: http://svn.apache.org/viewvc/directory/mavibot/branches/with-txns/mavibot/src/main/java/org/apache/directory/mavibot/btree/InMemoryBTree.java?rev=1566659&r1=1566658&r2=1566659&view=diff ============================================================================== --- directory/mavibot/branches/with-txns/mavibot/src/main/java/org/apache/directory/mavibot/btree/InMemoryBTree.java (original) +++ directory/mavibot/branches/with-txns/mavibot/src/main/java/org/apache/directory/mavibot/btree/InMemoryBTree.java Mon Feb 10 15:35:18 2014 @@ -29,6 +29,7 @@ import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.ReentrantLock; import org.apache.directory.mavibot.btree.exception.InitializationException; @@ -48,7 +49,7 @@ import org.slf4j.LoggerFactory; * * @author Apache Directory Project */ -/* No qualifier */class InMemoryBTree- IN_MEMORY : the BTree will remain in memory, and won't be persisted on disk
- *- PERSISTENT : the BTree is in memory, but will be persisted on disk
- *- MANAGED : the BTree is managed by a RecordManager, and some pages may + *
- IN_MEMORY : the B-tree will remain in memory, and won't be persisted on disk
+ *- BACKED_ON_DISK : the B-tree is in memory, but will be persisted on disk
+ *- PERSISTED : the B-tree is managed by a RecordManager, and some pages may * be swapped out from memory on demand
+ *- PERSISTED_SUB : The B-tree is a Persisted B-tree, but a Sub B-tree one
+ *- PERSISTED_MANAGEMENT : This is a Persisted B-tree used to manage the other B-trees
*extends AbstractBTree implements Closeable +/* No qualifier */class InMemoryBTree extends AbstractBTree implements TransactionManager, Closeable { /** The LoggerFactory used by this class */ protected static final Logger LOG = LoggerFactory.getLogger( InMemoryBTree.class ); @@ -72,10 +73,20 @@ import org.slf4j.LoggerFactory; /** The associated journal. If null, this is an in-memory btree */ private File journal; + /** The directory where the journal will be stored */ private File envDir; + /** The Journal channel */ private FileChannel journalChannel = null; + /** The current transaction */ + private WriteTransaction writeTransaction; + + /** A lock to protect the creation of the transaction */ + private ReentrantLock createTransaction = new ReentrantLock(); + + /** A flag set when we initiate an automatic transaction */ + private AtomicBoolean automaticTransaction = new AtomicBoolean( true ); /** * Creates a new BTree, with no initialization. @@ -83,7 +94,6 @@ import org.slf4j.LoggerFactory; /* no qualifier */InMemoryBTree() { super(); - btreeHeader = new BTreeHeader(); setType( BTreeTypeEnum.IN_MEMORY ); } @@ -97,9 +107,9 @@ import org.slf4j.LoggerFactory; /* no qualifier */InMemoryBTree( InMemoryBTreeConfiguration configuration ) { super(); - String name = configuration.getName(); + String btreeName = configuration.getName(); - if ( name == null ) + if ( btreeName == null ) { throw new IllegalArgumentException( "BTree name cannot be null" ); } @@ -111,29 +121,34 @@ import org.slf4j.LoggerFactory; envDir = new File( filePath ); } - btreeHeader = new BTreeHeader(); - btreeHeader.setName( name ); - btreeHeader.setPageSize( configuration.getPageSize() ); - - keySerializer = configuration.getKeySerializer(); - btreeHeader.setKeySerializerFQCN( keySerializer.getClass().getName() ); - - valueSerializer = configuration.getValueSerializer(); - btreeHeader.setValueSerializerFQCN( valueSerializer.getClass().getName() ); + // Store the configuration in the B-tree + setName( btreeName ); + setPageSize( configuration.getPageSize() ); + setKeySerializer( configuration.getKeySerializer() ); + setValueSerializer( configuration.getValueSerializer() ); + setAllowDuplicates( configuration.isAllowDuplicates() ); + setType( configuration.getType() ); readTimeOut = configuration.getReadTimeOut(); writeBufferSize = configuration.getWriteBufferSize(); - btreeHeader.setAllowDuplicates( configuration.isAllowDuplicates() ); - setType( configuration.getType() ); if ( keySerializer.getComparator() == null ) { throw new IllegalArgumentException( "Comparator should not be null" ); } + // Create the B-tree header + BTreeHeader newBtreeHeader = new BTreeHeader (); + // Create the first root page, with revision 0L. It will be empty // and increment the revision at the same time - rootPage = new InMemoryLeaf ( this ); + newBtreeHeader.setBTreeOffset( 0L ); + newBtreeHeader.setRevision( 0L ); + newBtreeHeader.setNbElems( 0L ); + newBtreeHeader.setRootPage( new InMemoryLeaf ( this ) ); + newBtreeHeader.setRootPageOffset( 0L ); + + btreeRevisions.put( 0L, newBtreeHeader ); // Now, initialize the BTree try @@ -152,7 +167,7 @@ import org.slf4j.LoggerFactory; * * @throws IOException If we get some exception while initializing the BTree */ - public void init() throws IOException + private void init() throws IOException { // if not in-memory then default to persist mode instead of managed if ( envDir != null ) @@ -160,13 +175,14 @@ import org.slf4j.LoggerFactory; if ( !envDir.exists() ) { boolean created = envDir.mkdirs(); + if ( !created ) { throw new IllegalStateException( "Could not create the directory " + envDir + " for storing data" ); } } - this.file = new File( envDir, btreeHeader.getName() + DATA_SUFFIX ); + this.file = new File( envDir, getName() + DATA_SUFFIX ); this.journal = new File( envDir, file.getName() + JOURNAL_SUFFIX ); setType( BTreeTypeEnum.BACKED_ON_DISK ); @@ -175,8 +191,6 @@ import org.slf4j.LoggerFactory; // Create the queue containing the pending read transactions readTransactions = new ConcurrentLinkedQueue >(); - writeLock = new ReentrantLock(); - // Check the files and create them if missing // Create the queue containing the modifications, if it's not a in-memory btree if ( getType() == BTreeTypeEnum.BACKED_ON_DISK ) @@ -202,6 +216,11 @@ import org.slf4j.LoggerFactory; else { setType( BTreeTypeEnum.IN_MEMORY ); + + // This is a new Btree, we have to store the BtreeHeader + BTreeHeader btreeHeader = new BTreeHeader (); + btreeHeader.setRootPage( new InMemoryLeaf ( this ) ); + storeRevision( btreeHeader ); } // Initialize the txnManager thread @@ -225,8 +244,6 @@ import org.slf4j.LoggerFactory; flush(); journalChannel.close(); } - - rootPage = null; } @@ -244,59 +261,78 @@ import org.slf4j.LoggerFactory; */ protected Tuple delete( K key, V value, long revision ) throws IOException { - writeLock.lock(); + boolean inTransaction = beginTransaction( revision ); - try + if ( revision == -1L ) { - // If the key exists, the existing value will be replaced. We store it - // to return it to the caller. - Tuple tuple = null; + revision = currentRevision.get() + 1; + } + + BTreeHeader oldBtreeHeader = getBtreeHeader(); + BTreeHeader newBtreeHeader = createNewBtreeHeader( oldBtreeHeader, revision ); + + // If the key exists, the existing value will be replaced. We store it + // to return it to the caller. + Tuple 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 result = rootPage.delete( revision, key, value, null, -1 ); + // Try to delete the entry starting from the root page. Here, the root + // page may be either a Node or a Leaf + DeleteResult result = getRootPage().delete( key, value, revision ); - if ( result instanceof NotPresentResult ) + if ( result instanceof NotPresentResult ) + { + // Key not found. + if ( !inTransaction ) { - // Key not found. - return null; + commit(); } - // Keep the oldRootPage so that we can later access it - Page oldRootPage = rootPage; + return null; + } - if ( result instanceof RemoveResult ) - { - // The element was found, and removed - RemoveResult removeResult = ( RemoveResult ) result; + // Keep the oldRootPage so that we can later access it + //Page oldRootPage = rootPage; - Page modifiedPage = removeResult.getModifiedPage(); + if ( result instanceof RemoveResult ) + { + // The element was found, and removed + RemoveResult removeResult = ( RemoveResult ) result; - // This is a new root - rootPage = modifiedPage; - tuple = removeResult.getRemovedElement(); - } + Page modifiedPage = removeResult.getModifiedPage(); - if ( withJournal ) - { - // Inject the modification into the modification queue - writeToJournal( new Deletion ( key ) ); - } + // This is a new root + newBtreeHeader.setRootPage( modifiedPage ); + tuple = removeResult.getRemovedElement(); + } - // Decrease the number of elements in the current tree if the deletion is successful - if ( tuple != null ) - { - btreeHeader.decrementNbElems(); - } + if ( withJournal ) + { + // Inject the modification into the modification queue + writeToJournal( new Deletion ( key ) ); + } - // Return the value we have found if it was modified - return tuple; + // Decrease the number of elements in the current tree if the deletion is successful + if ( tuple != null ) + { + newBtreeHeader.decrementNbElems(); } - finally + + // Return the value we have found if it was modified + if ( !inTransaction ) + { + storeRevision( newBtreeHeader ); + commit(); + } + + if ( oldBtreeHeader.getNbUsers() == 0 ) { - // See above - writeLock.unlock(); + synchronized ( btreeRevisions ) + { + btreeRevisions.remove( oldBtreeHeader.getRevision() ); + } } + + return tuple; } @@ -320,6 +356,18 @@ import org.slf4j.LoggerFactory; throw new IllegalArgumentException( "Key must not be null" ); } + // We have to start a new transaction, which will be committed or rollbacked + // locally. This will duplicate the current BtreeHeader during this phase. + boolean inTransaction = beginTransaction( revision ); + + if ( revision == -1L ) + { + revision = currentRevision.get() + 1; + } + + BTreeHeader oldBtreeHeader = getBtreeHeader(); + BTreeHeader newBtreeHeader = createNewBtreeHeader( oldBtreeHeader, revision ); + // If the key exists, the existing value will be replaced. We store it // to return it to the caller. V modifiedValue = null; @@ -327,7 +375,7 @@ import org.slf4j.LoggerFactory; // 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 result = rootPage.insert( revision, key, value ); + InsertResult result = newBtreeHeader.getRootPage().insert( key, value, revision ); if ( result instanceof ModifyResult ) { @@ -337,7 +385,7 @@ import org.slf4j.LoggerFactory; // The root has just been modified, we haven't split it // Get it and make it the current root page - rootPage = modifiedPage; + newBtreeHeader.setRootPage( modifiedPage ); modifiedValue = modifyResult.getModifiedValue(); } @@ -350,12 +398,9 @@ import org.slf4j.LoggerFactory; K pivot = splitResult.getPivot(); Page leftPage = splitResult.getLeftPage(); Page rightPage = splitResult.getRightPage(); - Page newRootPage = null; // Create the new rootPage - newRootPage = new InMemoryNode ( this, revision, pivot, leftPage, rightPage ); - - rootPage = newRootPage; + newBtreeHeader.setRootPage( new InMemoryNode ( this, revision, pivot, leftPage, rightPage ) ); } // Inject the modification into the modification queue @@ -368,7 +413,27 @@ import org.slf4j.LoggerFactory; // and does not replace an element if ( modifiedValue == null ) { - btreeHeader.incrementNbElems(); + newBtreeHeader.incrementNbElems(); + } + + if ( !inTransaction ) + { + storeRevision( newBtreeHeader ); + + commit(); + } + + if ( oldBtreeHeader.getNbUsers() == 0 ) + { + synchronized ( btreeRevisions ) + { + long oldRevision = oldBtreeHeader.getRevision(); + + if ( oldRevision < newBtreeHeader.getRevision() ) + { + btreeRevisions.remove( oldBtreeHeader.getRevision() ); + } + } } // Return the value we have found if it was modified @@ -456,7 +521,7 @@ import org.slf4j.LoggerFactory; } // Write the number of elements first - bb.putLong( btreeHeader.getNbElems() ); + bb.putLong( getBtreeHeader().getNbElems() ); while ( cursor.hasNext() ) { @@ -501,8 +566,6 @@ import org.slf4j.LoggerFactory; */ private void applyJournal() throws IOException { - long revision = generateRevision(); - if ( !journal.exists() ) { throw new IOException( "The journal does not exist" ); @@ -535,7 +598,7 @@ import org.slf4j.LoggerFactory; //values.add( value ); // Inject the data in the tree. (to be replaced by a bulk load) - insert( key, value, revision ); + insert( key, value, getBtreeHeader().getRevision() ); } else { @@ -543,7 +606,7 @@ import org.slf4j.LoggerFactory; K key = keySerializer.deserialize( bufferHandler ); // Remove the key from the tree - delete( key, revision ); + delete( key, getBtreeHeader().getRevision() ); } } } @@ -565,8 +628,6 @@ import org.slf4j.LoggerFactory; */ public void load( File file ) throws IOException { - long revision = generateRevision(); - if ( !file.exists() ) { throw new IOException( "The file does not exist" ); @@ -579,11 +640,6 @@ import org.slf4j.LoggerFactory; BufferHandler bufferHandler = new BufferHandler( channel, buffer ); long nbElems = LongSerializer.deserialize( bufferHandler.read( 8 ) ); - btreeHeader.setNbElems( nbElems ); - - // Prepare a list of keys and values read from the disk - //List keys = new ArrayList (); - //List values = new ArrayList (); // desactivate the journal while we load the file boolean isJournalActivated = withJournal; @@ -596,15 +652,11 @@ import org.slf4j.LoggerFactory; // Read the key K key = keySerializer.deserialize( bufferHandler ); - //keys.add( key ); - // Read the value V value = valueSerializer.deserialize( bufferHandler ); - //values.add( value ); - // Inject the data in the tree. (to be replaced by a bulk load) - insert( key, value, revision ); + insert( key, value, getBtreeHeader().getRevision() ); } // Restore the withJournal value @@ -616,7 +668,7 @@ import org.slf4j.LoggerFactory; /** - * Get the rootPzge associated to a give revision. + * Get the rootPage associated to a give revision. * * @param revision The revision we are looking for * @return The rootPage associated to this revision @@ -626,7 +678,24 @@ import org.slf4j.LoggerFactory; public Page getRootPage( long revision ) throws IOException, KeyNotFoundException { // Atm, the in-memory BTree does not support searches in many revisions - return rootPage; + return getBtreeHeader().getRootPage(); + } + + + /** + * Get the current rootPage + * + * @return The rootPage + */ + public Page getRootPage() + { + return getBtreeHeader().getRootPage(); + } + + + /* no qualifier */void setRootPage( Page root ) + { + getBtreeHeader().setRootPage( root ); } @@ -722,7 +791,53 @@ import org.slf4j.LoggerFactory; */ public void beginTransaction() { - // Does nothing... + automaticTransaction.set( false ); + beginTransaction( getRevision() + 1 ); + } + + + /** + * Starts a transaction + */ + private boolean beginTransaction( long revision ) + { + createTransaction.lock(); + + if ( writeTransaction == null ) + { + writeTransaction = new WriteTransaction(); + } + + if ( isTransactionStarted() && !automaticTransaction.get() ) + { + createTransaction.unlock(); + + return true; + } + + createTransaction.unlock(); + + writeTransaction.start(); + + return false; + } + + + /** + * Create a new B-tree header to be used for update operations + * @param revision The reclaimed revision + */ + private BTreeHeader createNewBtreeHeader( BTreeHeader btreeHeader, long revision ) + { + BTreeHeader newBtreeHeader = new BTreeHeader (); + + newBtreeHeader.setBTreeOffset( btreeHeader.getBTreeOffset() ); + newBtreeHeader.setRevision( revision ); + newBtreeHeader.setNbElems( btreeHeader.getNbElems() ); + newBtreeHeader.setRootPage( btreeHeader.getRootPage() ); + newBtreeHeader.setRootPageOffset( btreeHeader.getRootPageOffset() ); + + return newBtreeHeader; } @@ -731,7 +846,41 @@ import org.slf4j.LoggerFactory; */ public void commit() { - // Does nothing... + createTransaction.lock(); + + if ( writeTransaction == null ) + { + throw new RuntimeException( "Cannot commit a transaction which hasn't been started"); + } + + createTransaction.unlock(); + + writeTransaction.commit(); + automaticTransaction.set( true ); + } + + + /** + * Tells if a transaction has been started or not + */ + private boolean isTransactionStarted() + { + createTransaction.lock(); + + if ( writeTransaction == null ) + { + + } + boolean started = ( writeTransaction != null ) && ( writeTransaction.isStarted() ); + + if ( !started ) + { + + } + + createTransaction.unlock(); + + return started; } @@ -740,7 +889,16 @@ import org.slf4j.LoggerFactory; */ public void rollback() { - // Does nothing... + createTransaction.lock(); + + if ( writeTransaction == null ) + { + throw new RuntimeException( "Cannot rollback a transaction which hasn't been started"); + } + + createTransaction.unlock(); + + writeTransaction.rollback(); } @@ -760,16 +918,15 @@ import org.slf4j.LoggerFactory; case BACKED_ON_DISK: sb.append( "Persistent " ); break; - } sb.append( "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().getRootPage() != null ) { - sb.append( ", nbEntries:" ).append( btreeHeader.getNbElems() ); + sb.append( ", nbEntries:" ).append( getBtreeHeader().getNbElems() ); } else { @@ -787,7 +944,7 @@ import org.slf4j.LoggerFactory; sb.append( keySerializer.getComparator().getClass().getSimpleName() ); } - sb.append( ", DuplicatesAllowed: " ).append( btreeHeader.isAllowDuplicates() ); + sb.append( ", DuplicatesAllowed: " ).append( isAllowDuplicates() ); if ( getType() == BTreeTypeEnum.BACKED_ON_DISK ) { @@ -822,7 +979,7 @@ import org.slf4j.LoggerFactory; } sb.append( ") : \n" ); - sb.append( rootPage.dumpPage( "" ) ); + sb.append( getRootPage().dumpPage( "" ) ); return sb.toString(); } Modified: directory/mavibot/branches/with-txns/mavibot/src/main/java/org/apache/directory/mavibot/btree/InMemoryLeaf.java URL: http://svn.apache.org/viewvc/directory/mavibot/branches/with-txns/mavibot/src/main/java/org/apache/directory/mavibot/btree/InMemoryLeaf.java?rev=1566659&r1=1566658&r2=1566659&view=diff ============================================================================== --- directory/mavibot/branches/with-txns/mavibot/src/main/java/org/apache/directory/mavibot/btree/InMemoryLeaf.java (original) +++ directory/mavibot/branches/with-txns/mavibot/src/main/java/org/apache/directory/mavibot/btree/InMemoryLeaf.java Mon Feb 10 15:35:18 2014 @@ -72,7 +72,7 @@ import org.apache.directory.mavibot.btre /** * {@inheritDoc} */ - public InsertResult insert( long revision, K key, V value ) throws IOException + public InsertResult insert( K key, V value, long revision ) throws IOException { // Find the key into this leaf int pos = findPos( key ); @@ -117,7 +117,7 @@ import org.apache.directory.mavibot.btre * {@inheritDoc} */ @SuppressWarnings("unchecked") - public DeleteResult delete( long revision, K key, V value, Page parent, int parentPos ) + /* no qualifier */ DeleteResult delete( K key, V value, long revision, Page parent, int parentPos ) throws IOException { // Check that the leaf is not empty Modified: directory/mavibot/branches/with-txns/mavibot/src/main/java/org/apache/directory/mavibot/btree/InMemoryNode.java URL: http://svn.apache.org/viewvc/directory/mavibot/branches/with-txns/mavibot/src/main/java/org/apache/directory/mavibot/btree/InMemoryNode.java?rev=1566659&r1=1566658&r2=1566659&view=diff ============================================================================== --- directory/mavibot/branches/with-txns/mavibot/src/main/java/org/apache/directory/mavibot/btree/InMemoryNode.java (original) +++ directory/mavibot/branches/with-txns/mavibot/src/main/java/org/apache/directory/mavibot/btree/InMemoryNode.java Mon Feb 10 15:35:18 2014 @@ -28,7 +28,7 @@ import java.util.List; /** * A MVCC Node. It stores the keys and references to its children page. It does not * contain any value. - * + * * @param The type for the Key * @param The type for the stored value * @@ -40,7 +40,7 @@ import java.util.List; * Creates a new Node which will contain only one key, with references to * a left and right page. This is a specific constructor used by the btree * when the root was full when we added a new value. - * + * * @param btree the parent BTree * @param revision the Node revision * @param nbElems The number of elements in this Node @@ -59,7 +59,7 @@ import java.util.List; * Creates a new Node which will contain only one key, with references to * a left and right page. This is a specific constructor used by the btree * when the root was full when we added a new value. - * + * * @param btree the parent BTree * @param revision the Node revision * @param key The new key @@ -89,7 +89,7 @@ import java.util.List; /** * {@inheritDoc} */ - public InsertResult insert( long revision, K key, V value ) throws IOException + public InsertResult insert( K key, V value, long revision ) throws IOException { // Find the key into this leaf int pos = findPos( key ); @@ -105,7 +105,7 @@ import java.util.List; Page child = children[pos].getValue(); // and insert the into this child - InsertResult result = child.insert( revision, key, value ); + InsertResult result = child.insert( key, value, revision ); // Ok, now, we have injected the tuple down the tree. Let's check // the result to see if we have to split the current page @@ -145,7 +145,7 @@ import java.util.List; /** * Modifies the current node after a remove has been done in one of its children. * The node won't be merged with another node. - * + * * @param removeResult The result of a remove operation * @param index the position of the key, not transformed * @param pos The position of the key, as a positive value @@ -188,7 +188,7 @@ import java.util.List; /** * Handles the removal of an element from the root page, when two of its children * have been merged. - * + * * @param mergedResult The merge result * @param pos The position in the current root * @param found Tells if the removed key is present in the root page @@ -223,7 +223,7 @@ import java.util.List; * Borrows an element from the right sibling, creating a new sibling with one * less element and creating a new page where the element to remove has been * deleted and the borrowed element added on the right. - * + * * @param revision The new revision for all the pages * @param sibling The right sibling * @param pos The position of the element to remove @@ -308,7 +308,7 @@ import java.util.List; * Borrows an element from the left sibling, creating a new sibling with one * less element and creating a new page where the element to remove has been * deleted and the borrowed element added on the left. - * + * * @param revision The new revision for all the pages * @param sibling The left sibling * @param pos The position of the element to remove @@ -392,7 +392,7 @@ import java.util.List; /** * We have to merge the node with its sibling, both have N/2 elements before the element * removal. - * + * * @param revision The revision * @param mergedResult The result of the merge * @param sibling The Page we will merge the current page with @@ -523,7 +523,7 @@ import java.util.List; /** * {@inheritDoc} */ - public DeleteResult delete( long revision, K key, V value, Page parent, int parentPos ) + /* no qualifier */ DeleteResult delete( K key, V value, long revision, Page parent, int parentPos ) throws IOException { // We first try to delete the element from the child it belongs to @@ -538,12 +538,12 @@ import java.util.List; { index = -( pos + 1 ); child = children[-pos].getValue(); - deleteResult = child.delete( revision, key, value, this, -pos ); + deleteResult = ((AbstractPage )child).delete( key, value, revision, this, -pos ); } else { child = children[pos].getValue(); - deleteResult = child.delete( revision, key, value, this, pos ); + deleteResult = ((AbstractPage )child).delete( key, value, revision, this, pos ); } // If the key is not present in the tree, we simply return @@ -604,7 +604,7 @@ import java.util.List; // We will remove one element from a page that will have less than N/2 elements, // which will lead to some reorganization : either we can borrow an element from // a sibling, or we will have to merge two pages - int siblingPos = selectSibling( ( InMemoryNode ) parent, parentPos ); + int siblingPos = selectSibling( parent, parentPos ); InMemoryNode sibling = ( InMemoryNode ) ( ( ( InMemoryNode ) parent ).children[siblingPos] .getValue() ); @@ -717,7 +717,7 @@ import java.util.List; /** * Remove the key at a given position. - * + * * @param mergedResult The page we will remove a key from * @param revision The revision of the modified page * @param pos The position into the page of the element to remove @@ -780,7 +780,7 @@ import java.util.List; /** * Set the value at a give position - * + * * @param pos The position in the values array * @param value the value to inject */ @@ -794,7 +794,7 @@ import java.util.List; * This method is used when we have to replace a child in a page when we have * found the key in the tree (the value will be changed, so we have made * copies of the existing pages). - * + * * @param revision The current revision * @param result The modified page * @param pos The position of the found key @@ -825,7 +825,7 @@ import java.util.List; /** * Adds a new key into a copy of the current page at a given position. We return the * modified page. The new page will have one more key than the current page. - * + * * @param copiedPages the list of copied pages * @param revision The revision of the modified page * @param key The key to insert @@ -880,7 +880,7 @@ import java.util.List; * If the newly added element is in the middle, we will use it * as a pivot. Otherwise, we will use either the last element in the left page if the element is added * on the left, or the first element in the right page if it's added on the right. - * + * * @param copiedPages the list of copied pages * @param revision The new revision for all the created pages * @param pivot The key that will be move up after the split @@ -978,7 +978,7 @@ import java.util.List; /** * Copies the current page and all its keys, with a new revision. - * + * * @param revision The new revision * @return The copied page */ Added: directory/mavibot/branches/with-txns/mavibot/src/main/java/org/apache/directory/mavibot/btree/InMemoryTransactionManager.java URL: http://svn.apache.org/viewvc/directory/mavibot/branches/with-txns/mavibot/src/main/java/org/apache/directory/mavibot/btree/InMemoryTransactionManager.java?rev=1566659&view=auto ============================================================================== --- directory/mavibot/branches/with-txns/mavibot/src/main/java/org/apache/directory/mavibot/btree/InMemoryTransactionManager.java (added) +++ directory/mavibot/branches/with-txns/mavibot/src/main/java/org/apache/directory/mavibot/btree/InMemoryTransactionManager.java Mon Feb 10 15:35:18 2014 @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.directory.mavibot.btree; + +/** + * An implementation of a TransactionManager for in-memory B-trees + * + * @author Apache Directory Project + */ +public class InMemoryTransactionManager implements TransactionManager +{ + /** + * {@inheritDoc} + */ + @Override + public void beginTransaction() + { + } + + + /** + * {@inheritDoc} + */ + @Override + public void commit() + { + } + + + /** + * {@inheritDoc} + */ + @Override + public void rollback() + { + } +} Modified: directory/mavibot/branches/with-txns/mavibot/src/main/java/org/apache/directory/mavibot/btree/NameRevisionComparator.java URL: http://svn.apache.org/viewvc/directory/mavibot/branches/with-txns/mavibot/src/main/java/org/apache/directory/mavibot/btree/NameRevisionComparator.java?rev=1566659&r1=1566658&r2=1566659&view=diff ============================================================================== --- directory/mavibot/branches/with-txns/mavibot/src/main/java/org/apache/directory/mavibot/btree/NameRevisionComparator.java (original) +++ directory/mavibot/branches/with-txns/mavibot/src/main/java/org/apache/directory/mavibot/btree/NameRevisionComparator.java Mon Feb 10 15:35:18 2014 @@ -30,6 +30,17 @@ import java.util.Comparator; */ /* no qualifier*/class NameRevisionComparator implements Comparator { + /** A static instance of a NameRevisionComparator */ + public static final NameRevisionComparator INSTANCE = new NameRevisionComparator(); + + /** + * A private constructor of the NameRevisionComparator class + */ + private NameRevisionComparator() + { + } + + /** * {@inheritDoc} */ Modified: directory/mavibot/branches/with-txns/mavibot/src/main/java/org/apache/directory/mavibot/btree/NameRevisionSerializer.java URL: http://svn.apache.org/viewvc/directory/mavibot/branches/with-txns/mavibot/src/main/java/org/apache/directory/mavibot/btree/NameRevisionSerializer.java?rev=1566659&r1=1566658&r2=1566659&view=diff ============================================================================== --- directory/mavibot/branches/with-txns/mavibot/src/main/java/org/apache/directory/mavibot/btree/NameRevisionSerializer.java (original) +++ directory/mavibot/branches/with-txns/mavibot/src/main/java/org/apache/directory/mavibot/btree/NameRevisionSerializer.java Mon Feb 10 15:35:18 2014 @@ -41,12 +41,15 @@ import org.apache.directory.mavibot.btre */ /* no qualifier*/class NameRevisionSerializer extends AbstractElementSerializer { + /** A static instance of a NameRevisionSerializer */ + /*No qualifier*/ final static NameRevisionSerializer INSTANCE = new NameRevisionSerializer(); + /** * Create a new instance of a NameRevisionSerializer */ - /* no qualifier*/NameRevisionSerializer() + private NameRevisionSerializer() { - super( new NameRevisionComparator() ); + super( NameRevisionComparator.INSTANCE ); } Modified: directory/mavibot/branches/with-txns/mavibot/src/main/java/org/apache/directory/mavibot/btree/Page.java URL: http://svn.apache.org/viewvc/directory/mavibot/branches/with-txns/mavibot/src/main/java/org/apache/directory/mavibot/btree/Page.java?rev=1566659&r1=1566658&r2=1566659&view=diff ============================================================================== --- directory/mavibot/branches/with-txns/mavibot/src/main/java/org/apache/directory/mavibot/btree/Page.java (original) +++ directory/mavibot/branches/with-txns/mavibot/src/main/java/org/apache/directory/mavibot/btree/Page.java Mon Feb 10 15:35:18 2014 @@ -31,7 +31,7 @@ import org.apache.directory.mavibot.btre * (containing keys and references to child pages).
* A Page can be stored on disk. If so, we store the serialized value of this Page into * one or more {@link PageIO} (they will be linked) - * + * * @paramThe type for the Key * @param The type for the stored value * @@ -56,22 +56,22 @@ import org.apache.directory.mavibot.btre * the Page is full, we split it and propagate the new pivot up into the tree * * - * - * @param revision The new revision for the modified pages + * * @param key Inserted key * @param value Inserted value + * @param revision The new revision for the modified pages * @return Either a modified Page or an Overflow element if the Page was full * @throws IOException If we have an error while trying to access the page */ - InsertResult
insert( long revision, K key, V value ) throws IOException; + InsertResult insert( K key, V value, long revision ) throws IOException; /** - * Deletes the value from an entry associated with the given key in this page. We first find + * Deletes the value from an entry associated with the given key in this page. We first find * the place were to remove the into the tree, by recursively browsing the pages. - * If the value is present, it will be deleted first, later if there are no other values associated with + * If the value is present, it will be deleted first, later if there are no other values associated with * this key(which can happen when duplicates are enabled), we will remove the key from the tree. - * + * * @param revision The new revision for the modified pages * @param key The key to delete * @param value The value to delete (can be null) @@ -80,15 +80,15 @@ import org.apache.directory.mavibot.btre * @return Either a modified Page if the key has been removed from the page, or a NotPresentResult. * @throws IOException If we have an error while trying to access the page */ - DeleteResult delete( long revision, K key, V value, Page parent, int parentPos ) throws IOException; + DeleteResult delete( K key, V value, long revision /*, Page parent, int parentPos*/ ) throws IOException; /** - * Gets the value associated with the given key, if any. If we don't have + * Gets the value associated with the given key, if any. If we don't have * one, this method will throw a KeyNotFoundException.
- * Note that we may get back null if a null value has been associated + * Note that we may get back null if a null value has been associated * with the key. - * + * * @param key The key we are looking for * @return The associated value, which can be null * @throws KeyNotFoundException If no entry with the given key can be found @@ -98,23 +98,23 @@ import org.apache.directory.mavibot.btre /** - * Gets the values associated with the given key, if any. If we don't have + * Gets the values associated with the given key, if any. If we don't have * the key, this method will throw a KeyNotFoundException.
- * Note that we may get back null if a null value has been associated + * Note that we may get back null if a null value has been associated * with the key. - * + * * @param key The key we are looking for * @return The associated value, which can be null * @throws KeyNotFoundException If no entry with the given key can be found * @throws IOException If we have an error while trying to access the page - * @throws IllegalArgumentException If duplicates are not enabled + * @throws IllegalArgumentException If duplicates are not enabled */ ValueCursorgetValues( K key ) throws KeyNotFoundException, IOException, IllegalArgumentException; /** * Checks if the page contains the given key with the given value. - * + * * @param key The key we are looking for * @param value The value associated with the given key * @return true if the key and value are associated with each other, false otherwise @@ -125,7 +125,7 @@ import org.apache.directory.mavibot.btre /** * Browses the tree, looking for the given key, and creates a Cursor on top * of the found result. - * + * * @param key The key we are looking for. * @param transaction The started transaction for this operation * @param stack The stack of parents we go through to get to this page @@ -138,7 +138,7 @@ import org.apache.directory.mavibot.btre /** * Browses the whole tree, and creates a Cursor on top of it. - * + * * @param transaction The started transaction for this operation * @param stack The stack of parents we go through to get to this page * @return A Cursor to browse the next elements @@ -156,7 +156,7 @@ import org.apache.directory.mavibot.btre /** * Returns the key at a given position - * + * * @param pos The position of the key we want to retrieve * @return The key found at the given position */ @@ -166,7 +166,7 @@ import org.apache.directory.mavibot.btre /** * Finds the leftmost key in this page. If the page is a node, it will go * down in the leftmost children to recursively find the leftmost key. - * + * * @return The leftmost key in the tree */ K getLeftMostKey(); @@ -175,7 +175,7 @@ import org.apache.directory.mavibot.btre /** * Finds the rightmost key in this page. If the page is a node, it will go * down in the rightmost children to recursively find the rightmost key. - * + * * @return The rightmost key in the tree */ K getRightMostKey(); @@ -184,7 +184,7 @@ import org.apache.directory.mavibot.btre /** * Finds the leftmost element in this page. If the page is a node, it will go * down in the leftmost children to recursively find the leftmost element. - * + * * @return The leftmost element in the tree * @throws IOException If we have an error while trying to access the page */ @@ -194,7 +194,7 @@ import org.apache.directory.mavibot.btre /** * Finds the rightmost element in this page. If the page is a node, it will go * down in the rightmost children to recursively find the rightmost element. - * + * * @return The rightmost element in the tree * @throws IOException If we have an error while trying to access the page */ @@ -240,8 +240,8 @@ import org.apache.directory.mavibot.btre * 'h' will return -4 *'i' will return 4 * - * - * + * + * * @param key The key to find * @return The position in the page. */ @@ -250,7 +250,7 @@ import org.apache.directory.mavibot.btre /** * Checks if the given key exists. - * + * * @param key The key we are looking at * @return true if the key is present, false otherwise * @throws IOException If we have an error while trying to access the page Modified: directory/mavibot/branches/with-txns/mavibot/src/main/java/org/apache/directory/mavibot/btree/PageIO.java URL: http://svn.apache.org/viewvc/directory/mavibot/branches/with-txns/mavibot/src/main/java/org/apache/directory/mavibot/btree/PageIO.java?rev=1566659&r1=1566658&r2=1566659&view=diff ============================================================================== --- directory/mavibot/branches/with-txns/mavibot/src/main/java/org/apache/directory/mavibot/btree/PageIO.java (original) +++ directory/mavibot/branches/with-txns/mavibot/src/main/java/org/apache/directory/mavibot/btree/PageIO.java Mon Feb 10 15:35:18 2014 @@ -34,25 +34,25 @@ import org.apache.directory.mavibot.btre * Here is the logical structure of a PageIO : ** 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. *- * + * * @author Apache Directory Project */ /* 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:" ).append( Long.toHexString( offset ) ); if ( size != -1 ) {