Modified: directory/apacheds/branches/one-sub-level-index-removal/jdbm/src/main/java/jdbm/btree/BTree.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/one-sub-level-index-removal/jdbm/src/main/java/jdbm/btree/BTree.java?rev=1164667&r1=1164666&r2=1164667&view=diff
==============================================================================
--- directory/apacheds/branches/one-sub-level-index-removal/jdbm/src/main/java/jdbm/btree/BTree.java (original)
+++ directory/apacheds/branches/one-sub-level-index-removal/jdbm/src/main/java/jdbm/btree/BTree.java Fri Sep 2 18:30:57 2011
@@ -47,18 +47,27 @@
package jdbm.btree;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
+import java.io.ObjectInputStream;
import java.io.ObjectOutput;
+import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Comparator;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+import jdbm.ActionRecordManager;
import jdbm.RecordManager;
+import jdbm.helper.ActionContext;
import jdbm.helper.Serializer;
import jdbm.helper.Tuple;
import jdbm.helper.TupleBrowser;
+import jdbm.helper.WrappedRuntimeException;
import org.apache.directory.server.i18n.I18n;
@@ -77,7 +86,6 @@ import org.apache.directory.server.i18n.
* user is responsible to supply a serializable Comparator object
* to be used for the ordering of entries, which are also called Tuple.
* The B+Tree allows traversing the keys in forward and reverse order using a
- * TupleBrowser obtained from the browse() methods.
*
* This implementation does not directly support duplicate keys, but it is
* possible to handle duplicates by inlining or referencing an object collection
@@ -106,7 +114,7 @@ public class BTree implements Exte
private transient long recordId;
/** Comparator used to index entries. */
- private Comparator comparator;
+ Comparator comparator;
/** Serializer used to serialize index keys (optional) */
protected Serializer keySerializer;
@@ -118,10 +126,10 @@ public class BTree implements Exte
* Height of the B+Tree. This is the number of BPages you have to traverse
* to get to a leaf BPage, starting from the root.
*/
- private int bTreeHeight;
+ int bTreeHeight;
/** Record id of the root BPage */
- private transient long rootId;
+ private long rootId;
/** Number of entries in each BPage. */
protected int pageSize;
@@ -131,7 +139,16 @@ public class BTree implements Exte
/** Serializer used for BPages of this tree */
private transient BPage bpageSerializer;
-
+
+ /** TRUE if underlying record manager is snapshot capable */
+ private transient boolean isActionCapable;
+
+
+ /** Big lock snychronizing all actions */
+ private transient Lock bigLock = new ReentrantLock();
+
+ /** Meta root used to access versions of Btree root */
+ private transient MetaRoot metaRoot = new MetaRoot();
/**
* No-argument constructor used by serialization.
@@ -220,8 +237,27 @@ public class BTree implements Exte
this.bpageSerializer = new BPage();
this.bpageSerializer.btree = this;
this.nbEntries = new AtomicInteger( 0 );
+
+ this.isActionCapable = recordManager instanceof ActionRecordManager;
- this.recordId = recordManager.insert( this );
+ boolean abortedAction = false;
+ ActionContext context = this.beginAction( false, "createInstance" );
+ try
+ {
+ this.recordId = recordManager.insert( this );
+ updateMetaRoot( this.rootId, this.bTreeHeight );
+ }
+ catch ( IOException e )
+ {
+ abortedAction = true;
+ this.abortAction( context );
+ throw e;
+ }
+ finally
+ {
+ if ( !abortedAction )
+ this.endAction( context );
+ }
}
@@ -246,11 +282,33 @@ public class BTree implements Exte
*/
public BTree load( RecordManager recman, long recid ) throws IOException
{
- BTree btree = (BTree) recman.fetch( recid );
- btree.recordId = recid;
- btree.recordManager = recman;
- btree.bpageSerializer = new BPage();
- btree.bpageSerializer.btree = btree;
+ BTree btree = null;
+ boolean abortedAction = false;
+ ActionContext context = this.beginAction( false, "load" );
+
+ try
+ {
+ btree = (BTree) recman.fetch( recid );
+ btree.recordId = recid;
+ btree.recordManager = recman;
+ btree.bpageSerializer = new BPage();
+ btree.bpageSerializer.btree = btree;
+ btree.updateMetaRoot( btree.rootId, btree.bTreeHeight );
+
+ }
+ catch ( IOException e )
+ {
+ abortedAction = true;
+ this.abortAction( context );
+ throw e;
+ }
+ finally
+ {
+ if ( !abortedAction )
+ {
+ this.endAction( context );
+ }
+ }
return btree;
}
@@ -268,7 +326,7 @@ public class BTree implements Exte
* @param replace Set to true to replace an existing key-value pair.
* @return Existing value, if any.
*/
- public synchronized Object insert( K key, V value, boolean replace ) throws IOException
+ public Object insert( K key, V value, boolean replace ) throws IOException
{
if ( key == null )
{
@@ -280,56 +338,95 @@ public class BTree implements Exte
throw new IllegalArgumentException( I18n.err( I18n.ERR_524 ) );
}
- BPage rootPage = getRoot();
+
+ boolean abortedAction = false;
+ ActionContext context = this.beginAction( false, "insert" );
+
- if ( rootPage == null )
+ if ( !isActionCapable )
{
- // BTree is currently empty, create a new root BPage
- if ( DEBUG )
- {
- System.out.println( "BTree.insert() new root BPage" );
- }
-
- rootPage = new BPage( this, key, value );
- rootId = rootPage.getRecordId();
- bTreeHeight = 1;
- nbEntries.set( 1 );
- recordManager.update( recordId, this );
-
- return null;
+ bigLock.lock();
}
- else
+
+ try
{
- BPage.InsertResult insert = rootPage.insert( bTreeHeight, key, value, replace );
- boolean dirty = false;
-
- if ( insert.overflow != null )
+ BPage rootPage = getRoot();
+
+ if ( rootPage == null )
{
- // current root page overflowed, we replace with a new root page
+ // BTree is currently empty, create a new root BPage
if ( DEBUG )
{
- System.out.println( "BTree.insert() replace root BPage due to overflow" );
+ System.out.println( "BTree.insert() new root BPage" );
}
- rootPage = new BPage( this, rootPage, insert.overflow );
+ rootPage = new BPage( this, key, value );
rootId = rootPage.getRecordId();
- bTreeHeight += 1;
- dirty = true;
+ bTreeHeight = 1;
+ nbEntries.set( 1 );
+ recordManager.update( recordId, this );
+ updateMetaRoot( this.rootId, this.bTreeHeight );
+
+ return null;
}
-
- if ( insert.existing == null )
+ else
{
- nbEntries.getAndIncrement();
- dirty = true;
+ BPage.InsertResult insert = rootPage.insert( bTreeHeight, key, value, replace );
+
+ if ( insert.pageNewCopy != null )
+ {
+ rootPage = insert.pageNewCopy;
+ }
+
+ boolean dirty = false;
+
+ if ( insert.overflow != null )
+ {
+ // current root page overflowed, we replace with a new root page
+ if ( DEBUG )
+ {
+ System.out.println( "BTree.insert() replace root BPage due to overflow" );
+ }
+
+ rootPage = new BPage( this, rootPage, insert.overflow );
+ rootId = rootPage.getRecordId();
+ bTreeHeight += 1;
+ dirty = true;
+ updateMetaRoot( this.rootId, this.bTreeHeight );
+ }
+
+ if ( insert.existing == null )
+ {
+ nbEntries.getAndIncrement();
+ dirty = true;
+ }
+
+ if ( dirty )
+ {
+ recordManager.update( recordId, this );
+ }
+
+ // insert might have returned an existing value
+ return insert.existing;
}
-
- if ( dirty )
+ }
+ catch ( IOException e )
+ {
+ abortedAction = true;
+ this.abortAction( context );
+ throw e;
+ }
+ finally
+ {
+ if ( !abortedAction )
{
- recordManager.update( recordId, this );
+ this.endAction( context );
}
- // insert might have returned an existing value
- return insert.existing;
+ if ( !isActionCapable )
+ {
+ bigLock.unlock();
+ }
}
}
@@ -341,52 +438,88 @@ public class BTree implements Exte
* @return Value associated with the key, or null if no entry with given
* key existed in the BTree.
*/
- public synchronized V remove( K key ) throws IOException
+ public V remove( K key ) throws IOException
{
if ( key == null )
{
throw new IllegalArgumentException( I18n.err( I18n.ERR_523 ) );
}
-
- BPage rootPage = getRoot();
+
- if ( rootPage == null )
+ boolean abortedAction = false;
+ ActionContext context = this.beginAction( false, "remove" );
+
+ if ( !isActionCapable )
{
- return null;
+ bigLock.lock();
}
- boolean dirty = false;
- BPage.RemoveResult remove = rootPage.remove( bTreeHeight, key );
-
- if ( remove.underflow && rootPage.isEmpty() )
+ try
{
- bTreeHeight -= 1;
- dirty = true;
-
- recordManager.delete( rootId );
+ BPage rootPage = getRoot();
- if ( bTreeHeight == 0 )
+ if ( rootPage == null )
{
- rootId = 0;
+ return null;
}
- else
+
+ boolean dirty = false;
+ BPage.RemoveResult remove = rootPage.remove( bTreeHeight, key );
+
+ if ( remove.pageNewCopy != null )
{
- rootId = rootPage.childBPage( pageSize - 1 ).getRecordId();
+ rootPage = remove.pageNewCopy;
}
+
+ if ( remove.underflow && rootPage.isEmpty() )
+ {
+ bTreeHeight -= 1;
+ dirty = true;
+
+ recordManager.delete( rootId );
+
+ if ( bTreeHeight == 0 )
+ {
+ rootId = 0;
+ }
+ else
+ {
+ rootId = rootPage.childBPage( pageSize - 1 ).getRecordId();
+ }
+ updateMetaRoot( this.rootId, this.bTreeHeight );
+ }
+
+ if ( remove.value != null )
+ {
+ nbEntries.getAndDecrement();
+ dirty = true;
+ }
+
+ if ( dirty )
+ {
+ recordManager.update( recordId, this );
+ }
+
+ return remove.value;
}
-
- if ( remove.value != null )
+ catch ( IOException e )
{
- nbEntries.getAndDecrement();
- dirty = true;
+ abortedAction = true;
+ this.abortAction( context );
+ throw e;
}
-
- if ( dirty )
+ finally
{
- recordManager.update( recordId, this );
+ if ( !abortedAction )
+ {
+ this.endAction( context );
+ }
+
+ if ( !isActionCapable )
+ {
+ bigLock.unlock();
+ }
}
-
- return remove.value;
}
@@ -396,40 +529,58 @@ public class BTree implements Exte
* @param key Lookup key.
* @return Value associated with the key, or null if not found.
*/
- public synchronized V find( K key ) throws IOException
+ public V find( K key ) throws IOException
{
+ TupleBrowser browser = null;
+ Tuple tuple = null;
+
if ( key == null )
{
throw new IllegalArgumentException( I18n.err( I18n.ERR_523 ) );
}
- BPage rootPage = getRoot();
-
- if ( rootPage == null )
+ if ( !isActionCapable )
{
- return null;
+ bigLock.lock();
}
-
- Tuple tuple = new Tuple( null, null );
- TupleBrowser browser = rootPage.find( bTreeHeight, key );
-
- if ( browser.getNext( tuple ) )
- {
- // find returns the matching key or the next ordered key, so we must
- // check if we have an exact match
- if ( comparator.compare( key, tuple.getKey() ) != 0 )
+
+ try
+ {
+ tuple = new Tuple( null, null );
+
+ browser = browse( key );
+
+ if ( browser.getNext( tuple ) )
{
- return null;
+ // find returns the matching key or the next ordered key, so we must
+ // check if we have an exact match
+ if ( comparator.compare( key, tuple.getKey() ) != 0 )
+ {
+ return null;
+ }
+ else
+ {
+ return this.copyValue( tuple.getValue() );
+ }
}
else
{
- return tuple.getValue();
+ return null;
}
}
- else
+ finally
{
- return null;
+ if ( browser != null )
+ {
+ browser.close();
+ }
+
+ if ( !isActionCapable )
+ {
+ bigLock.unlock();
+ }
}
+
}
@@ -441,10 +592,10 @@ public class BTree implements Exte
* @return Value associated with the key, or a greater entry, or null if no
* greater entry was found.
*/
- public synchronized Tuple findGreaterOrEqual( K key ) throws IOException
+ public Tuple findGreaterOrEqual( K key ) throws IOException
{
Tuple tuple;
- TupleBrowser browser;
+ TupleBrowser browser = null;
if ( key == null )
{
@@ -453,16 +604,38 @@ public class BTree implements Exte
return null;
}
+ if ( !isActionCapable )
+ {
+ bigLock.lock();
+ }
+
tuple = new Tuple( null, null );
- browser = browse( key );
- if ( browser.getNext( tuple ) )
+ try
{
- return tuple;
+ browser = browse( key );
+
+ if ( browser.getNext( tuple ) )
+ {
+ tuple.setValue( this.copyValue( tuple.getValue() ) );
+ return tuple;
+ }
+ else
+ {
+ return null;
+ }
}
- else
+ finally
{
- return null;
+ if ( browser != null )
+ {
+ browser.close();
+ }
+
+ if ( !isActionCapable )
+ {
+ bigLock.unlock();
+ }
}
}
@@ -476,17 +649,31 @@ public class BTree implements Exte
*
* @return Browser positionned at the beginning of the BTree.
*/
- public synchronized TupleBrowser browse() throws IOException
+ public TupleBrowser browse() throws IOException
{
- BPage rootPage = getRoot();
+ TupleBrowser browser = null;
+ ActionContext context = this.beginAction( true, "browse" );
- if ( rootPage == null )
+ try
{
- return new EmptyBrowser(){};
+ MetaRoot meta = this.getMetaRoot();
+ BPage rootPage = this.getRoot( meta );
+
+ if ( rootPage == null )
+ {
+ this.endAction( context );
+ return new EmptyBrowser(){};
+ }
+
+ browser = rootPage.findFirst( context );
+ }
+ catch( IOException e )
+ {
+ this.abortAction( context );
+ throw e;
}
- TupleBrowser browser = rootPage.findFirst();
-
+ this.unsetAsCurrentAction( context );
return browser;
}
@@ -503,19 +690,34 @@ public class BTree implements Exte
* (Null is considered to be an "infinite" key)
* @return Browser positioned just before the given key.
*/
- public synchronized TupleBrowser browse( K key ) throws IOException
+ public TupleBrowser browse( K key ) throws IOException
{
- BPage rootPage = getRoot();
+ TupleBrowser browser = null;
+ ActionContext context = this.beginAction( true, "browse key" );
- if ( rootPage == null )
+ try
{
- return new EmptyBrowser(){};
+ MetaRoot meta = this.getMetaRoot();
+ BPage rootPage = this.getRoot( meta );
+
+ if ( rootPage == null )
+ {
+ this.endAction( context );
+ return new EmptyBrowser(){};
+ }
+
+ browser = rootPage.find( meta.treeHeight, key, context );
+ }
+ catch( IOException e )
+ {
+ this.abortAction( context );
+ throw e;
}
- TupleBrowser browser = rootPage.find( bTreeHeight, key );
-
+ this.unsetAsCurrentAction( context );
return browser;
}
+
/**
@@ -537,23 +739,65 @@ public class BTree implements Exte
/**
- * Return the root BPage