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, or null if it doesn't exist. + * @return the root BPage, or null if it doesn't exist. + */ + BPage getRoot( ) throws IOException + { + assert( this.rootId == metaRoot.rootID) : "Stale root id " + this.rootId + " "+ metaRoot.rootID; + + if ( this.rootId == 0 ) + { + return null; + } + + BPage root = ( BPage ) recordManager.fetch( this.rootId, bpageSerializer ); + root.setRecordId( this.rootId ); + root.btree = this; + + return root; + } + + + /** + * @param meta The root to search for + * + * @return the root BPage, or null if it doesn't exist. */ - private BPage getRoot() throws IOException + BPage getRoot( MetaRoot meta ) throws IOException { - if ( rootId == 0 ) + if ( meta.rootID == 0 ) { return null; } - BPage root = ( BPage ) recordManager.fetch( rootId, bpageSerializer ); - root.setRecordId( rootId ); + BPage root = ( BPage ) recordManager.fetch( meta.rootID, bpageSerializer ); + root.setRecordId( meta.rootID ); root.btree = this; return root; } - - + + + /** + * + * Returns the meta root that can be used to fetch the root page + * + * @return meta root The meta root to search for + * @throws IOException If we had an exception during the fetch operation + */ + MetaRoot getMetaRoot() throws IOException + { + if ( isActionCapable ) + { + return ( MetaRoot )recordManager.fetch( -this.recordId ); + } + else + { + return metaRoot; + } + } + + /** * Implement Externalizable interface. */ @@ -616,6 +860,151 @@ public class BTree implements Exte } + void setAsCurrentAction( ActionContext context ) + { + if ( context != null ) + { + assert( isActionCapable == true ); + ( ( ActionRecordManager )recordManager ).setCurrentActionContext( context ); + } + } + + + void unsetAsCurrentAction( ActionContext context ) + { + if ( context != null ) + { + assert( isActionCapable == true ); + ( ( ActionRecordManager )recordManager ).unsetCurrentActionContext( context ); + } + } + + + ActionContext beginAction( boolean readOnly, String whoStarted ) + { + ActionContext context = null; + + if ( isActionCapable ) + { + context = ( ( ActionRecordManager )recordManager ).beginAction( readOnly, whoStarted ); + } + + return context; + } + + + void endAction( ActionContext context ) + { + if ( context != null ) + { + assert( isActionCapable ); + ( ( ActionRecordManager )recordManager ).endAction( context ); + } + } + + + void abortAction( ActionContext context ) + { + if ( context != null ) + { + assert( isActionCapable ); + ( ( ActionRecordManager )recordManager ).abortAction( context ); + } + + } + + + BPage copyOnWrite( BPage page) throws IOException + { + return page.copyOnWrite(); + + } + + + private MetaRoot copyOnWrite( MetaRoot oldMetaRoot ) + { + MetaRoot newMetaRoot = new MetaRoot(); + newMetaRoot.rootID = oldMetaRoot.rootID; + newMetaRoot.treeHeight = oldMetaRoot.treeHeight; + + return newMetaRoot; + } + + + private void updateMetaRoot( long newRootId, int newTreeHeight ) throws IOException + { + metaRoot = this.copyOnWrite( metaRoot ); + metaRoot.rootID = newRootId; + metaRoot.treeHeight = newTreeHeight; + + if ( isActionCapable ) + { + recordManager.update( -this.recordId, metaRoot ); + } + } + + + V copyValue( V value) throws IOException + { + byte[] array; + V valueCopy = null; + + if ( this.valueSerializer != null ) + { + array = this.valueSerializer.serialize( value ); + valueCopy = (V) this.valueSerializer.deserialize( array ); + } + else + { + ObjectOutputStream out = null; + ObjectInputStream in = null; + ByteArrayOutputStream bout = null; + ByteArrayInputStream bin = null; + + try + { + bout = new ByteArrayOutputStream(); + out = new ObjectOutputStream( bout ); + out.writeObject( value ); + out.flush(); + byte[] arr = bout.toByteArray(); + bin = new ByteArrayInputStream( arr ); + in =new ObjectInputStream( bin ); + valueCopy = ( V )in.readObject(); + } + catch ( ClassNotFoundException e ) + { + throw new WrappedRuntimeException( e ); + } + finally + { + if ( bout != null ) + { + bout.close(); + } + + if ( out != null ) + { + out.close(); + } + + if ( bin != null ) + { + bin.close(); + } + + if ( in != null ) + { + in.close(); + } + } + + } + + return valueCopy; + } + + public String toString() { StringBuilder sb = new StringBuilder(); @@ -662,4 +1051,14 @@ public class BTree implements Exte return sb.toString(); } + + /** + * Used to point to the root page that the reader needs based on the reader's + * read action context. ReadWrite actions always use the latest root. + */ + class MetaRoot + { + long rootID; + int treeHeight; + } } Modified: directory/apacheds/branches/one-sub-level-index-removal/jdbm/src/main/java/jdbm/helper/TupleBrowser.java URL: http://svn.apache.org/viewvc/directory/apacheds/branches/one-sub-level-index-removal/jdbm/src/main/java/jdbm/helper/TupleBrowser.java?rev=1164667&r1=1164666&r2=1164667&view=diff ============================================================================== --- directory/apacheds/branches/one-sub-level-index-removal/jdbm/src/main/java/jdbm/helper/TupleBrowser.java (original) +++ directory/apacheds/branches/one-sub-level-index-removal/jdbm/src/main/java/jdbm/helper/TupleBrowser.java Fri Sep 2 18:30:57 2011 @@ -73,4 +73,11 @@ public abstract class TupleBrowser * no previous tuple. */ public abstract boolean getPrevious( Tuple tuple ) throws IOException; + + + /** + * Closes the browser and deallocates any resources it might have allocated. + * Repeated calls of close are OK. + */ + public void close() {} } Modified: directory/apacheds/branches/one-sub-level-index-removal/jdbm/src/main/java/jdbm/recman/BaseRecordManager.java URL: http://svn.apache.org/viewvc/directory/apacheds/branches/one-sub-level-index-removal/jdbm/src/main/java/jdbm/recman/BaseRecordManager.java?rev=1164667&r1=1164666&r2=1164667&view=diff ============================================================================== --- directory/apacheds/branches/one-sub-level-index-removal/jdbm/src/main/java/jdbm/recman/BaseRecordManager.java (original) +++ directory/apacheds/branches/one-sub-level-index-removal/jdbm/src/main/java/jdbm/recman/BaseRecordManager.java Fri Sep 2 18:30:57 2011 @@ -49,15 +49,18 @@ package jdbm.recman; import java.io.IOException; - import java.util.HashMap; import java.util.Map; - -import org.apache.directory.server.i18n.I18n; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; import jdbm.RecordManager; -import jdbm.helper.Serializer; import jdbm.helper.DefaultSerializer; +import jdbm.helper.Serializer; + +import org.apache.directory.server.i18n.I18n; /** * This class manages records, which are uninterpreted blobs of data. The @@ -81,10 +84,8 @@ import jdbm.helper.DefaultSerializer; * @author Alex Boisvert * @author Cees de Groot */ -public final class BaseRecordManager - implements RecordManager +public final class BaseRecordManager implements RecordManager { - /** Underlying record recordFile. */ private RecordFile recordFile; @@ -109,6 +110,102 @@ public final class BaseRecordManager * the NAME_DIRECTORY_ROOT. */ private Map nameDirectory; + + private static enum IOType + { + READ_IO, + WRITE_IO + } + + /** TODO add asserts to check internal consistency */ + private static class LockElement + { + private int readers; + private int waiters; + private boolean writer; + + private Lock lock = new ReentrantLock(); + private Condition cv = lock.newCondition(); + + + public boolean anyReaders() + { + return readers > 0; + } + + + public boolean anyWaiters() + { + return waiters > 0; + } + + + public boolean beingWritten() + { + return writer; + } + + + public boolean anyUser() + { + return ( readers > 0 || waiters > 0 || writer ); + } + + + public void bumpReaders() + { + readers++; + } + + + public void decrementReaders() + { + readers--; + } + + + public void bumpWaiters() + { + waiters++; + } + + + public void decrementWaiters() + { + waiters--; + } + + + public void setWritten() + { + writer = true; + } + + + public void unsetWritten() + { + writer = false; + } + + + public Lock getLock() + { + return lock; + } + + + public Condition getNoConflictingIOCondition() + { + return cv; + } + } + + + /** + * Map used to synchronize reads and writes on the same logical + * recid. + */ + private final ConcurrentHashMap lockElements; /** @@ -123,13 +220,14 @@ public final class BaseRecordManager pageMgr = new PageManager( recordFile ); physMgr = new PhysicalRowIdManager( pageMgr ); logMgr = new LogicalRowIdManager( pageMgr ); + lockElements = new ConcurrentHashMap(); } /** * Get the underlying Transaction Manager */ - public synchronized TransactionManager getTransactionManager() throws IOException + public TransactionManager getTransactionManager() throws IOException { checkIfClosed(); return recordFile.getTxnMgr(); @@ -145,7 +243,7 @@ public final class BaseRecordManager * Only call this method directly after opening the file, otherwise * the results will be undefined. */ - public synchronized void disableTransactions() + public void disableTransactions() { checkIfClosed(); recordFile.disableTransactions(); @@ -157,7 +255,7 @@ public final class BaseRecordManager * * @throws IOException when one of the underlying I/O operations fails. */ - public synchronized void close() throws IOException + public void close() throws IOException { checkIfClosed(); @@ -190,7 +288,7 @@ public final class BaseRecordManager * @return the rowid for the new record. * @throws IOException when one of the underlying I/O operations fails. */ - public synchronized long insert( Object obj, Serializer serializer ) throws IOException + public long insert( Object obj, Serializer serializer ) throws IOException { byte[] data; long recid; @@ -217,8 +315,9 @@ public final class BaseRecordManager * @param recid the rowid for the record that should be deleted. * @throws IOException when one of the underlying I/O operations fails. */ - public synchronized void delete( long recid ) throws IOException + public void delete( long recid ) throws IOException { + LockElement element; checkIfClosed(); if ( recid <= 0 ) @@ -231,10 +330,20 @@ public final class BaseRecordManager System.out.println( "BaseRecordManager.delete() recid " + recid ) ; } - Location logRowId = new Location( recid ); - Location physRowId = logMgr.fetch( logRowId ); - physMgr.delete( physRowId ); - logMgr.delete( logRowId ); + + element = this.beginIO(recid, IOType.WRITE_IO); + + try + { + Location logRowId = new Location( recid ); + Location physRowId = logMgr.fetch( logRowId ); + physMgr.delete( physRowId ); + logMgr.delete( logRowId ); + } + finally + { + this.endIO(recid, element, IOType.WRITE_IO); + } } @@ -249,8 +358,8 @@ public final class BaseRecordManager { update( recid, obj, DefaultSerializer.INSTANCE ); } - + /** * Updates a record using a custom serializer. * @@ -259,8 +368,10 @@ public final class BaseRecordManager * @param serializer a custom serializer * @throws IOException when one of the underlying I/O operations fails. */ - public synchronized void update( long recid, Object obj, Serializer serializer ) throws IOException + public void update( long recid, Object obj, Serializer serializer ) throws IOException { + LockElement element; + checkIfClosed(); if ( recid <= 0 ) @@ -268,24 +379,33 @@ public final class BaseRecordManager throw new IllegalArgumentException( I18n.err( I18n.ERR_536, recid ) ); } - Location logRecid = new Location( recid ); - Location physRecid = logMgr.fetch( logRecid ); - - byte[] data = serializer.serialize( obj ); - - if ( DEBUG ) - { - System.out.println( "BaseRecordManager.update() recid " + recid + " length " + data.length ) ; - } - - Location newRecid = physMgr.update( physRecid, data, 0, data.length ); - - if ( ! newRecid.equals( physRecid ) ) - { - logMgr.update( logRecid, newRecid ); - } + element = this.beginIO(recid, IOType.WRITE_IO); + + try + { + Location logRecid = new Location( recid ); + Location physRecid = logMgr.fetch( logRecid ); + + byte[] data = serializer.serialize( obj ); + + if ( DEBUG ) + { + System.out.println( "BaseRecordManager.update() recid " + recid + " length " + data.length ) ; + } + + Location newRecid = physMgr.update( physRecid, data, 0, data.length ); + + if ( ! newRecid.equals( physRecid ) ) + { + logMgr.update( logRecid, newRecid ); + } + } + finally + { + this.endIO(recid, element, IOType.WRITE_IO); + } } - + /** * Fetches a record using standard java object serialization. @@ -299,7 +419,7 @@ public final class BaseRecordManager return fetch( recid, DefaultSerializer.INSTANCE ); } - + /** * Fetches a record using a custom serializer. * @@ -308,25 +428,40 @@ public final class BaseRecordManager * @return the object contained in the record. * @throws IOException when one of the underlying I/O operations fails. */ - public synchronized Object fetch( long recid, Serializer serializer ) - throws IOException + public Object fetch( long recid, Serializer serializer ) throws IOException { - byte[] data; - + Object result; + LockElement element; + checkIfClosed(); - + if ( recid <= 0 ) { throw new IllegalArgumentException( I18n.err( I18n.ERR_536, recid ) ); } - data = physMgr.fetch( logMgr.fetch( new Location( recid ) ) ); + element = this.beginIO(recid, IOType.READ_IO); - if ( DEBUG ) + try + { + byte[] data; + + data = physMgr.fetch( logMgr.fetch( new Location( recid ) ) ); + + if ( DEBUG ) + { + System.out.println( "BaseRecordManager.fetch() recid " + recid + " length " + data.length ) ; + } + + result = serializer.deserialize( data ); + } + finally { - System.out.println( "BaseRecordManager.fetch() recid " + recid + " length " + data.length ) ; + this.endIO(recid, element, IOType.READ_IO); } - return serializer.deserialize( data ); + + + return result; } @@ -347,7 +482,7 @@ public final class BaseRecordManager * * @see #getRootCount */ - public synchronized long getRoot( int id ) throws IOException + public long getRoot( int id ) throws IOException { checkIfClosed(); @@ -360,7 +495,7 @@ public final class BaseRecordManager * * @see #getRootCount */ - public synchronized void setRoot( int id, long rowid ) throws IOException + public void setRoot( int id, long rowid ) throws IOException { checkIfClosed(); @@ -404,6 +539,7 @@ public final class BaseRecordManager { getNameDirectory().put( name, recid ); } + saveNameDirectory( ); } @@ -411,8 +547,7 @@ public final class BaseRecordManager /** * Commit (make persistent) all changes since beginning of transaction. */ - public synchronized void commit() - throws IOException + public void commit() throws IOException { checkIfClosed(); @@ -423,7 +558,7 @@ public final class BaseRecordManager /** * Rollback (cancel) all changes since beginning of transaction. */ - public synchronized void rollback() throws IOException + public void rollback() throws IOException { checkIfClosed(); @@ -478,4 +613,163 @@ public final class BaseRecordManager throw new IllegalStateException( I18n.err( I18n.ERR_538 ) ); } } + + + /** + * Used to serialize reads/write on a given logical rowid. Checks if there is a + * ongoing conflicting IO to the same logical rowid and waits for the ongoing + * write if there is one. + * + * @param recid the logical rowid for which the fetch will be done. + * @param io type of the IO + * @return lock element representing the logical lock gotten + */ + private LockElement beginIO( Long recid, IOType io ) + { + boolean lockVerified = false; + LockElement element = null; + + // loop until we successfully verify that there is no concurrent writer +/* + element = lockElements.get( recid ); + + do + { + if ( element == null ) + { + element = new LockElement(); + + if ( io == IOType.READ_IO ) + { + element.bumpReaders(); + } + else + { + element.setWritten(); + } + + LockElement existingElement = lockElements.putIfAbsent( recid, element ); + + if ( existingElement == null ) + { + lockVerified = true; + } + else + { + element = existingElement; + } + } + else + { + Lock lock = element.getLock(); + lock.lock(); + + if ( element.anyUser() ) + { + if ( this.conflictingIOPredicate( io, element ) ) + { + element.bumpWaiters(); + + do + { + element.getNoConflictingIOCondition() + .awaitUninterruptibly(); + } + while ( this.conflictingIOPredicate( io, element ) ); + + element.decrementWaiters(); + } + + // no conflicting IO anymore..done + if ( io == IOType.READ_IO ) + { + element.bumpReaders(); + } + else + { + element.setWritten(); + } + + lockVerified = true; + } + else + { + if ( io == IOType.READ_IO ) + { + element.bumpReaders(); + } + else + { + element.setWritten(); + } + + LockElement existingElement = lockElements.get( recid ); + + if ( element != existingElement ) + { + element = existingElement; + } + else + { + lockVerified = true; // done + } + } + + lock.unlock(); + } + } + while ( !lockVerified ); +*/ + return element; + } + + + /** + * Ends the IO by releasing the logical lock on the given recid + * + * @param recid logical recid for which the IO is being ended + * @param element logical lock to be released + * @param io type of the io + */ + private void endIO( Long recid, LockElement element, IOType io ) + { + /* + Lock lock = element.getLock(); + lock.lock(); + + if ( io == IOType.READ_IO ) + { + element.decrementReaders(); + } + else + { + element.unsetWritten(); + } + + if ( element.anyWaiters() ) + { + element.getNoConflictingIOCondition().notifyAll(); + } + + if ( !element.anyUser() ) + { + lockElements.remove( recid ); + } + + lock.unlock(); + */ + } + + + private boolean conflictingIOPredicate( IOType io, LockElement element ) + { + if ( io == IOType.READ_IO ) + { + return element.beingWritten(); + } + else + { + return ( element.anyReaders() || element.beingWritten() ); + } + } } Modified: directory/apacheds/branches/one-sub-level-index-removal/jdbm/src/main/java/jdbm/recman/BlockIo.java URL: http://svn.apache.org/viewvc/directory/apacheds/branches/one-sub-level-index-removal/jdbm/src/main/java/jdbm/recman/BlockIo.java?rev=1164667&r1=1164666&r2=1164667&view=diff ============================================================================== --- directory/apacheds/branches/one-sub-level-index-removal/jdbm/src/main/java/jdbm/recman/BlockIo.java (original) +++ directory/apacheds/branches/one-sub-level-index-removal/jdbm/src/main/java/jdbm/recman/BlockIo.java Fri Sep 2 18:30:57 2011 @@ -50,6 +50,8 @@ package jdbm.recman; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; +import java.util.concurrent.atomic.AtomicInteger; + import org.apache.directory.server.i18n.I18n; @@ -73,15 +75,16 @@ public final class BlockIo implements ja private long blockId; /** The row data contained in this block */ - private transient byte[] data; + private byte[] data; - private transient BlockView view = null; + /** A view on the BlockIo */ + private BlockView view = null; /** A flag set when this block has been modified */ - private transient boolean dirty = false; + private boolean dirty = false; /** The number of pending transaction on this block */ - private transient int transactionCount = 0; + private AtomicInteger transactionCount = new AtomicInteger( 0 ); /** @@ -99,7 +102,7 @@ public final class BlockIo implements ja * @param blockId The identifier for this block * @param data The data to store */ - BlockIo( long blockId, byte[] data ) + /*No qualifier*/ BlockIo( long blockId, byte[] data ) { // remove me for production version if ( blockId < 0 ) @@ -115,7 +118,7 @@ public final class BlockIo implements ja /** * @return the underlying array */ - byte[] getData() + /*No qualifier*/ byte[] getData() { return data; } @@ -126,7 +129,7 @@ public final class BlockIo implements ja * * @param The block identifier */ - void setBlockId( long blockId ) + /*No qualifier*/ void setBlockId( long blockId ) { if ( isInTransaction() ) { @@ -145,7 +148,7 @@ public final class BlockIo implements ja /** * @return the block number. */ - long getBlockId() + /*No qualifier*/ long getBlockId() { return blockId; } @@ -162,6 +165,8 @@ public final class BlockIo implements ja /** * Sets the current view of the block. + * + * @param view the current view */ public void setView( BlockView view ) { @@ -172,7 +177,7 @@ public final class BlockIo implements ja /** * Sets the dirty flag */ - void setDirty() + /*No qualifier*/ void setDirty() { dirty = true; } @@ -181,7 +186,7 @@ public final class BlockIo implements ja /** * Clears the dirty flag */ - void setClean() + /*No qualifier*/ void setClean() { dirty = false; } @@ -190,7 +195,7 @@ public final class BlockIo implements ja /** * Returns true if the dirty flag is set. */ - boolean isDirty() + /*No qualifier*/ boolean isDirty() { return dirty; } @@ -200,9 +205,9 @@ public final class BlockIo implements ja * Returns true if the block is still dirty with respect to the * transaction log. */ - boolean isInTransaction() + /*No qualifier*/ boolean isInTransaction() { - return transactionCount != 0; + return transactionCount.get() != 0; } @@ -211,12 +216,9 @@ public final class BlockIo implements ja * block is in the log but not yet in the data recordFile. The method also * takes a snapshot so that the data may be modified in new transactions. */ - synchronized void incrementTransactionCount() + /*No qualifier*/ void incrementTransactionCount() { - transactionCount++; - - // @fix me ( alex ) - setClean(); + transactionCount.getAndIncrement(); } @@ -224,11 +226,9 @@ public final class BlockIo implements ja * Decrements transaction count for this block, to signal that this * block has been written from the log to the data recordFile. */ - synchronized void decrementTransactionCount() + /*No qualifier*/ void decrementTransactionCount() { - transactionCount--; - - if ( transactionCount < 0 ) + if ( transactionCount.decrementAndGet() < 0 ) { throw new Error( I18n.err( I18n.ERR_541, getBlockId() ) ); } @@ -237,6 +237,9 @@ public final class BlockIo implements ja /** * Reads a byte from the indicated position + * + * @param pos the position at which we will read the byte + * @return the read byte */ public byte readByte( int pos ) { @@ -246,51 +249,66 @@ public final class BlockIo implements ja /** * Writes a byte to the indicated position + * + * @param pos The position where we want to write the value to + * @param value the byte value we want to write into the BlockIo */ public void writeByte( int pos, byte value ) { data[pos] = value; - setDirty(); + dirty = true; } /** * Reads a short from the indicated position + * + * @param pos the position at which we will read the short + * @return the read short */ public short readShort( int pos ) { return ( short ) - ( ( ( short ) ( data[pos+0] & 0xff ) << 8 ) | - ( ( short ) ( data[pos+1] & 0xff ) << 0 ) ); + ( ( ( data[pos+0] & 0xff ) << 8 ) | + ( ( data[pos+1] & 0xff ) << 0 ) ); } /** * Writes a short to the indicated position + * + * @param pos The position where we want to write the value to + * @param value the short value we want to write into the BlockIo */ public void writeShort( int pos, short value ) { data[pos+0] = ( byte ) ( 0xff & ( value >> 8 ) ); data[pos+1] = ( byte ) ( 0xff & ( value >> 0 ) ); - setDirty(); + dirty = true; } /** * Reads an int from the indicated position + * + * @param pos the position at which we will read the int + * @return the read int */ public int readInt( int pos ) { return - ( ( ( int ) ( data[pos+0] & 0xff ) << 24) | - ( ( int ) ( data[pos+1] & 0xff ) << 16) | - ( ( int ) ( data[pos+2] & 0xff ) << 8) | - ( ( int ) ( data[pos+3] & 0xff ) << 0 ) ); + ( data[pos+0] << 24) | + ( ( data[pos+1] & 0xff ) << 16) | + ( ( data[pos+2] & 0xff ) << 8) | + ( ( data[pos+3] & 0xff ) << 0 ); } /** * Writes an int to the indicated position + * + * @param pos The position where we want to write the value to + * @param value the int value we want to write into the BlockIo */ public void writeInt( int pos, int value ) { @@ -298,31 +316,35 @@ public final class BlockIo implements ja data[pos+1] = ( byte ) ( 0xff & ( value >> 16 ) ); data[pos+2] = ( byte ) ( 0xff & ( value >> 8 ) ); data[pos+3] = ( byte ) ( 0xff & ( value >> 0 ) ); - setDirty(); + dirty = true; } /** * Reads a long from the indicated position + * + * @param pos the position at which we will read the long + * @return the read long */ public long readLong( int pos ) { - // Contributed by Erwin Bolwidt - // Gives about 15% performance improvement return - ( ( long )( ( ( data[pos+0] & 0xff ) << 24 ) | - ( ( data[pos+1] & 0xff ) << 16 ) | - ( ( data[pos+2] & 0xff ) << 8 ) | - ( ( data[pos+3] & 0xff ) ) ) << 32 ) | - ( ( long )( ( ( data[pos+4] & 0xff ) << 24 ) | - ( ( data[pos+5] & 0xff ) << 16 ) | - ( ( data[pos+6] & 0xff ) << 8 ) | - ( ( data[pos+7] & 0xff ) ) ) & 0xffffffff ); + ( ( long )( (long)data[pos+0] << 56 ) | + ( (long)( data[pos+1] & 0xff ) << 48 ) | + ( (long)( data[pos+2] & 0xff ) << 40 ) | + ( (long)( data[pos+3] & 0xff ) << 32 ) | + ( (long)( data[pos+4] & 0xff ) << 24 ) | + ( (long)( data[pos+5] & 0xff ) << 16 ) | + ( (long)( data[pos+6] & 0xff ) << 8 ) | + ( (long)( data[pos+7] & 0xff ) ) ); } /** * Writes a long to the indicated position + * + * @param pos The position where we want to write the value to + * @param value the long value we want to write into the BlockIo */ public void writeLong(int pos, long value) { data[pos+0] = (byte)(0xff & (value >> 56)); @@ -333,23 +355,59 @@ public final class BlockIo implements ja data[pos+5] = (byte)(0xff & (value >> 16)); data[pos+6] = (byte)(0xff & (value >> 8)); data[pos+7] = (byte)(0xff & (value >> 0)); - setDirty(); + dirty = true; } - // overrides java.lang.Object - + /** + * {@inheritDoc} + */ public String toString() { - return "BlockIO ( " - + blockId + ", " - + dirty + ", " - + view + " )"; + if ( view != null ) + { + return view.toString(); + } + + StringBuilder sb = new StringBuilder(); + + sb.append( "BlockIO ( " ); + + // The blockID + sb.append( blockId ).append( ", " ); + + // Is it dirty ? + if ( dirty ) + { + sb.append( "dirty, " ); + } + else + { + sb.append( "clean, " ); + } + + // The view + if ( view != null ) + { + sb.append( view.getClass().getSimpleName() ).append( ", " ); + } + else + { + sb.append( "no view, " ); + } + + // The transaction count + sb.append( "tx: " ).append( transactionCount.get() ); + + sb.append( " )" ); + + return sb.toString(); } - // implement externalizable interface - + /** + * implement externalizable interface + */ public void readExternal( ObjectInput in ) throws IOException, ClassNotFoundException { blockId = in.readLong(); @@ -359,7 +417,9 @@ public final class BlockIo implements ja } - // implement externalizable interface + /** + * implement externalizable interface + */ public void writeExternal( ObjectOutput out ) throws IOException { out.writeLong( blockId ); Modified: directory/apacheds/branches/one-sub-level-index-removal/jdbm/src/main/java/jdbm/recman/DataPage.java URL: http://svn.apache.org/viewvc/directory/apacheds/branches/one-sub-level-index-removal/jdbm/src/main/java/jdbm/recman/DataPage.java?rev=1164667&r1=1164666&r2=1164667&view=diff ============================================================================== --- directory/apacheds/branches/one-sub-level-index-removal/jdbm/src/main/java/jdbm/recman/DataPage.java (original) +++ directory/apacheds/branches/one-sub-level-index-removal/jdbm/src/main/java/jdbm/recman/DataPage.java Fri Sep 2 18:30:57 2011 @@ -47,6 +47,7 @@ package jdbm.recman; + import org.apache.directory.server.i18n.I18n; @@ -56,14 +57,13 @@ import org.apache.directory.server.i18n. final class DataPage extends PageHeader { // offsets - /** first short in the file after the page header info: 18 byte offset */ private static final short O_FIRST = PageHeader.SIZE; // short firstrowid /** start of the data in this block: 20 byte offset */ static final short O_DATA = ( short ) ( O_FIRST + Magic.SZ_SHORT ); - /** total amount of data in this page/block: 8172 bytes */ + /** total amount of data in this page/block: BLOCK_SIZE - 20 bytes */ static final short DATA_PER_PAGE = ( short ) ( RecordFile.BLOCK_SIZE - O_DATA ); @@ -79,36 +79,67 @@ final class DataPage extends PageHeader /** * Factory method to create or return a data page for the indicated block. */ - static DataPage getDataPageView( BlockIo block ) + static DataPage getDataPageView( BlockIo blockIo ) { - BlockView view = block.getView(); - if ( view != null && view instanceof DataPage ) + BlockView view = blockIo.getView(); + + if ( ( view != null ) && ( view instanceof DataPage ) ) { return ( DataPage ) view; } else { - return new DataPage( block ); + return new DataPage( blockIo ); } } - /** Returns the first rowid's offset */ + /** + * @return the first rowid's offset + */ short getFirst() { - return block.readShort( O_FIRST ); + return blockIo.readShort( O_FIRST ); } - /** Sets the first rowid's offset */ + /** + * Sets the first rowid's offset + */ void setFirst( short value ) { paranoiaMagicOk(); + if ( value > 0 && value < O_DATA ) { throw new Error( I18n.err( I18n.ERR_543, value ) ); } - block.writeShort( O_FIRST, value ); + blockIo.writeShort( O_FIRST, value ); + } + + + /** + * {@inheritDoc} + */ + public String toString() + { + StringBuilder sb = new StringBuilder(); + + sb.append( "DataPage ( " ); + + // The blockIO + sb.append( super.toString() ).append( ", " ); + + // The first rowId + sb.append( "first rowId: " ).append( getFirst() ).append( ", " ); + + // The data per page + sb.append( "[p:" ).append( getPrev() ).append( ", " ); + + // The next page + sb.append( "n:" ).append( getNext() ).append( "] )" ); + + return sb.toString(); } } Modified: directory/apacheds/branches/one-sub-level-index-removal/jdbm/src/main/java/jdbm/recman/FreeLogicalRowIdPage.java URL: http://svn.apache.org/viewvc/directory/apacheds/branches/one-sub-level-index-removal/jdbm/src/main/java/jdbm/recman/FreeLogicalRowIdPage.java?rev=1164667&r1=1164666&r2=1164667&view=diff ============================================================================== --- directory/apacheds/branches/one-sub-level-index-removal/jdbm/src/main/java/jdbm/recman/FreeLogicalRowIdPage.java (original) +++ directory/apacheds/branches/one-sub-level-index-removal/jdbm/src/main/java/jdbm/recman/FreeLogicalRowIdPage.java Fri Sep 2 18:30:57 2011 @@ -47,6 +47,7 @@ package jdbm.recman; + /** * Class describing a page that holds logical rowids that were freed. Note * that the methods have *physical* rowids in their signatures - this is @@ -55,107 +56,187 @@ package jdbm.recman; */ class FreeLogicalRowIdPage extends PageHeader { - // offsets + /** The offset for the number of free pages */ private static final short O_COUNT = PageHeader.SIZE; // short count + + /** Offset of the number of free row Ids */ static final short O_FREE = O_COUNT + Magic.SZ_SHORT; + + /** The number of elements by page */ static final short ELEMS_PER_PAGE = ( RecordFile.BLOCK_SIZE - O_FREE ) / PhysicalRowId.SIZE; - // slots we returned. + /** */ final PhysicalRowId[] slots = new PhysicalRowId[ELEMS_PER_PAGE]; /** * Constructs a data page view from the indicated block. */ - FreeLogicalRowIdPage( BlockIo block ) + FreeLogicalRowIdPage( BlockIo blockIo ) { - super( block ); + super( blockIo ); } /** * Factory method to create or return a data page for the indicated block. */ - static FreeLogicalRowIdPage getFreeLogicalRowIdPageView( BlockIo block ) + static FreeLogicalRowIdPage getFreeLogicalRowIdPageView( BlockIo blockIo ) { - BlockView view = block.getView(); - if ( view != null && view instanceof FreeLogicalRowIdPage ) + BlockView view = blockIo.getView(); + + if ( ( view != null ) && ( view instanceof FreeLogicalRowIdPage ) ) { return ( FreeLogicalRowIdPage ) view; } else { - return new FreeLogicalRowIdPage(block); + return new FreeLogicalRowIdPage( blockIo ); } } - /** Returns the number of free rowids */ - short getCount() { - return block.readShort(O_COUNT); + /** + * @return the number of free rowids + */ + short getCount() + { + return blockIo.readShort( O_COUNT ); } + - /** Sets the number of free rowids */ - private void setCount(short i) { - block.writeShort(O_COUNT, i); + /** + * Sets the number of free rowids + */ + private void setCount( short i ) + { + blockIo.writeShort( O_COUNT, i ); } + - /** Frees a slot */ - void free(int slot) { - get(slot).setBlock(0); - setCount((short) (getCount() - 1)); + /** + * Frees a slot + */ + void free( int slot ) + { + get( slot ).setBlock( 0 ); + setCount( (short) ( getCount() - 1 ) ); } + - /** Allocates a slot */ - PhysicalRowId alloc(int slot) { - setCount((short) (getCount() + 1)); - get(slot).setBlock(-1); - return get(slot); + /** + * Allocates a slot + */ + PhysicalRowId alloc( int slot ) + { + setCount( (short) ( getCount() + 1 ) ); + get( slot ).setBlock( -1 ); + + return get( slot ); } + - /** Returns true if a slot is allocated */ - boolean isAllocated(int slot) { - return get(slot).getBlock() > 0; + /** + * Returns true if a slot is allocated + */ + private boolean isAllocated( int slot ) + { + return get( slot ).getBlock() > 0; } + - /** Returns true if a slot is free */ - boolean isFree(int slot) { + /** + * Returns true if a slot is free + */ + private boolean isFree( int slot ) + { return !isAllocated(slot); } - /** Returns the value of the indicated slot */ - PhysicalRowId get(int slot) { - if (slots[slot] == null) - slots[slot] = new PhysicalRowId(block, slotToOffset(slot)); + /** + * Returns the value of the indicated slot + */ + PhysicalRowId get( int slot ) + { + if ( slots[slot] == null ) + { + slots[slot] = new PhysicalRowId( blockIo, slotToOffset( slot ) ); + } + return slots[slot]; } + - /** Converts slot to offset */ - private short slotToOffset(int slot) { - return (short) (O_FREE + - (slot * PhysicalRowId.SIZE)); + /** + * Converts slot to offset + */ + private short slotToOffset( int slot ) + { + return (short) ( O_FREE + ( slot * PhysicalRowId.SIZE ) ); } + /** * Returns first free slot, -1 if no slots are available */ - int getFirstFree() { - for (int i = 0; i < ELEMS_PER_PAGE; i++) { - if (isFree(i)) + int getFirstFree() + { + for ( int i = 0; i < ELEMS_PER_PAGE; i++ ) + { + if ( isFree( i ) ) + { return i; + } } + return -1; } + /** - * Returns first allocated slot, -1 if no slots are available. + * @return The first allocated slot, -1 if no slots are available. */ - int getFirstAllocated() { - for (int i = 0; i < ELEMS_PER_PAGE; i++) { - if (isAllocated(i)) + int getFirstAllocated() + { + for ( int i = 0; i < ELEMS_PER_PAGE; i++ ) + { + if ( isAllocated( i ) ) + { return i; + } } + return -1; } + + + /** + * {@inheritDoc} + */ + public String toString() + { + StringBuilder sb = new StringBuilder(); + + sb.append( "FreeLogRowIdPage ( " ); + + // The blockIO + sb.append( super.toString() ).append( ", " ); + + // The first rowId + sb.append( "count: " ).append( getCount() ); + + // Dump the Physical row id + for ( int i = 0; i < ELEMS_PER_PAGE; i++ ) + { + if ( slots[i] != null ) + { + sb.append( ", [" ).append( i ).append( "]=<" ).append( slots[i].getBlock() ).append( ", " ).append( slots[i].getOffset() ).append( ">" ); + } + } + + sb.append( ")" ); + + return sb.toString(); + } } Modified: directory/apacheds/branches/one-sub-level-index-removal/jdbm/src/main/java/jdbm/recman/FreeLogicalRowIdPageManager.java URL: http://svn.apache.org/viewvc/directory/apacheds/branches/one-sub-level-index-removal/jdbm/src/main/java/jdbm/recman/FreeLogicalRowIdPageManager.java?rev=1164667&r1=1164666&r2=1164667&view=diff ============================================================================== --- directory/apacheds/branches/one-sub-level-index-removal/jdbm/src/main/java/jdbm/recman/FreeLogicalRowIdPageManager.java (original) +++ directory/apacheds/branches/one-sub-level-index-removal/jdbm/src/main/java/jdbm/recman/FreeLogicalRowIdPageManager.java Fri Sep 2 18:30:57 2011 @@ -53,90 +53,119 @@ import java.io.IOException; * This class manages free Logical rowid pages and provides methods * to free and allocate Logical rowids on a high level. */ -final class FreeLogicalRowIdPageManager { - // our record recordFile +final class FreeLogicalRowIdPageManager +{ + /** our record recordFile */ private RecordFile recordFile; - // our page manager + + /** our page manager */ private PageManager pageManager; /** * Creates a new instance using the indicated record file and * page manager. */ - FreeLogicalRowIdPageManager( PageManager pageManager) throws IOException { + FreeLogicalRowIdPageManager( PageManager pageManager) throws IOException + { this.pageManager = pageManager; this.recordFile = pageManager.getRecordFile(); } + /** - * Returns a free Logical rowid, or - * null if nothing was found. + * Returns a free Logical rowid, or null if nothing was found. */ - Location get() throws IOException { - + Location get() throws IOException + { // Loop through the free Logical rowid list until we find // the first rowid. - Location retval = null; - PageCursor curs = new PageCursor(pageManager, Magic.FREELOGIDS_PAGE); - while (curs.next() != 0) { - FreeLogicalRowIdPage fp = FreeLogicalRowIdPage - .getFreeLogicalRowIdPageView(recordFile.get(curs.getCurrent())); + // Create a cursor to browse the pages + PageCursor cursor = new PageCursor( pageManager, Magic.FREELOGIDS_PAGE ); + + // Loop on the pages now + while ( cursor.next() != 0 ) + { + // Get the blockIo associated with the blockId + BlockIo blockIo = recordFile.get( cursor.getBlockId() ); + FreeLogicalRowIdPage fp = FreeLogicalRowIdPage.getFreeLogicalRowIdPageView( blockIo ); + + // Get the first allocated FreeLogicalRowId int slot = fp.getFirstAllocated(); - if (slot != -1) { + + if ( slot != -1 ) + { // got one! - retval = - new Location(fp.get(slot)); - fp.free(slot); - if (fp.getCount() == 0) { + Location location = new Location( fp.get( slot ) ); + + // Remove the block from the page + fp.free( slot ); + + boolean hasMore = fp.getCount() != 0; + + // Upate the recordFile + recordFile.release( cursor.getBlockId(), hasMore ); + + if ( !hasMore ) + { // page became empty - free it - recordFile.release(curs.getCurrent(), false); - pageManager.free(Magic.FREELOGIDS_PAGE, curs.getCurrent()); + pageManager.free( Magic.FREELOGIDS_PAGE, cursor.getBlockId() ); } - else - recordFile.release(curs.getCurrent(), true); - return retval; + return location; } - else { + else + { // no luck, go to next page - recordFile.release(curs.getCurrent(), false); - } + recordFile.release( cursor.getBlockId(), false ); + } } + return null; } + /** * Puts the indicated rowid on the free list + * + * @param rowId The Location where we will store the rowId */ - void put(Location rowid) - throws IOException { + void put( Location rowId ) throws IOException + { PhysicalRowId free = null; - PageCursor curs = new PageCursor(pageManager, Magic.FREELOGIDS_PAGE); + + // Create a cursor on the FREELOGIDs list + PageCursor curs = new PageCursor( pageManager, Magic.FREELOGIDS_PAGE ); long freePage = 0; - while (curs.next() != 0) { - freePage = curs.getCurrent(); - BlockIo curBlock = recordFile.get(freePage); - FreeLogicalRowIdPage fp = FreeLogicalRowIdPage - .getFreeLogicalRowIdPageView(curBlock); + + // Loop on all the list + while ( curs.next() != 0 ) + { + freePage = curs.getBlockId(); + BlockIo curBlockIo = recordFile.get( freePage ); + FreeLogicalRowIdPage fp = FreeLogicalRowIdPage.getFreeLogicalRowIdPageView( curBlockIo ); int slot = fp.getFirstFree(); - if (slot != -1) { + + if ( slot != -1 ) + { free = fp.alloc(slot); break; } - recordFile.release(curBlock); + recordFile.release( curBlockIo ); } - if (free == null) { + + if ( free == null ) + { // No more space on the free list, add a page. - freePage = pageManager.allocate(Magic.FREELOGIDS_PAGE); - BlockIo curBlock = recordFile.get(freePage); - FreeLogicalRowIdPage fp = - FreeLogicalRowIdPage.getFreeLogicalRowIdPageView(curBlock); - free = fp.alloc(0); + freePage = pageManager.allocate( Magic.FREELOGIDS_PAGE ); + BlockIo curBlockIo = recordFile.get( freePage ); + FreeLogicalRowIdPage fp = FreeLogicalRowIdPage.getFreeLogicalRowIdPageView( curBlockIo ); + free = fp.alloc( 0 ); } - free.setBlock(rowid.getBlock()); - free.setOffset(rowid.getOffset()); - recordFile.release(freePage, true); + + free.setBlock( rowId.getBlock() ); + free.setOffset( rowId.getOffset() ); + recordFile.release( freePage, true ); } -} +} \ No newline at end of file Modified: directory/apacheds/branches/one-sub-level-index-removal/jdbm/src/main/java/jdbm/recman/FreePhysicalRowIdPage.java URL: http://svn.apache.org/viewvc/directory/apacheds/branches/one-sub-level-index-removal/jdbm/src/main/java/jdbm/recman/FreePhysicalRowIdPage.java?rev=1164667&r1=1164666&r2=1164667&view=diff ============================================================================== --- directory/apacheds/branches/one-sub-level-index-removal/jdbm/src/main/java/jdbm/recman/FreePhysicalRowIdPage.java (original) +++ directory/apacheds/branches/one-sub-level-index-removal/jdbm/src/main/java/jdbm/recman/FreePhysicalRowIdPage.java Fri Sep 2 18:30:57 2011 @@ -47,6 +47,7 @@ package jdbm.recman; + /** * Class describing a page that holds physical rowids that were freed. */ @@ -76,6 +77,7 @@ final class FreePhysicalRowIdPage extend static FreePhysicalRowIdPage getFreePhysicalRowIdPageView( BlockIo block ) { BlockView view = block.getView(); + if ( view != null && view instanceof FreePhysicalRowIdPage ) { return ( FreePhysicalRowIdPage ) view; @@ -87,21 +89,27 @@ final class FreePhysicalRowIdPage extend } - /** Returns the number of free rowids */ + /** + * Returns the number of free rowids + */ short getCount() { - return block.readShort( O_COUNT ); + return blockIo.readShort( O_COUNT ); } - /** Sets the number of free rowids */ + /** + * Sets the number of free rowids + */ private void setCount( short i ) { - block.writeShort( O_COUNT, i ); + blockIo.writeShort( O_COUNT, i ); } - /** Frees a slot */ + /** + * Frees a slot + */ void free( int slot ) { get( slot ).setSize( 0 ); @@ -109,7 +117,9 @@ final class FreePhysicalRowIdPage extend } - /** Allocates a slot */ + /** + * Allocates a slot + */ FreePhysicalRowId alloc( int slot ) { setCount( ( short ) ( getCount() + 1 ) ); @@ -117,33 +127,41 @@ final class FreePhysicalRowIdPage extend } - /** Returns true if a slot is allocated */ + /** + * Returns true if a slot is allocated + */ boolean isAllocated( int slot ) { return get( slot ).getSize() != 0; } - /** Returns true if a slot is free */ + /** + * Returns true if a slot is free + */ boolean isFree( int slot ) { return ! isAllocated( slot ); } - /** Returns the value of the indicated slot */ + /** + * Returns the value of the indicated slot + */ FreePhysicalRowId get( int slot ) { if ( slots[slot] == null ) { - slots[slot] = new FreePhysicalRowId( block, slotToOffset( slot ) ) ; + slots[slot] = new FreePhysicalRowId( blockIo, slotToOffset( slot ) ) ; } return slots[slot]; } - /** Converts slot to offset */ + /** + * Converts slot to offset + */ short slotToOffset( int slot ) { return ( short ) ( O_FREE + ( slot * FreePhysicalRowId.SIZE ) ); @@ -151,7 +169,7 @@ final class FreePhysicalRowIdPage extend /** - * Returns first free slot, -1 if no slots are available + * @return first free slot, -1 if no slots are available */ int getFirstFree() { @@ -168,7 +186,7 @@ final class FreePhysicalRowIdPage extend /** - * Returns first slot with available size >= indicated size, or -1 if no + * @return first slot with available size >= indicated size, or -1 if no * slots are available. */ int getFirstLargerThan( int size ) @@ -183,4 +201,37 @@ final class FreePhysicalRowIdPage extend return -1; } + + + /** + * {@inheritDoc} + */ + public String toString() + { + StringBuilder sb = new StringBuilder(); + + sb.append( "FreePhysRowIdPage ( " ); + + // The blockIO + sb.append( super.toString() ).append( ", " ); + + // The first rowId + sb.append( "count: " ).append( getCount() ); + + // Dump the Physical row id + for ( int i = 0; i < ELEMS_PER_PAGE; i++ ) + { + if ( slots[i] != null ) + { + sb.append( ", [" ).append( i ).append( "]=<" ). + append( slots[i].getBlock() ).append( ", " ). + append( slots[i].getOffset() ).append( ", " ). + append( slots[i].getSize() ).append( ">" ); + } + } + + sb.append( ")" ); + + return sb.toString(); + } } \ No newline at end of file Modified: directory/apacheds/branches/one-sub-level-index-removal/jdbm/src/main/java/jdbm/recman/FreePhysicalRowIdPageManager.java URL: http://svn.apache.org/viewvc/directory/apacheds/branches/one-sub-level-index-removal/jdbm/src/main/java/jdbm/recman/FreePhysicalRowIdPageManager.java?rev=1164667&r1=1164666&r2=1164667&view=diff ============================================================================== --- directory/apacheds/branches/one-sub-level-index-removal/jdbm/src/main/java/jdbm/recman/FreePhysicalRowIdPageManager.java (original) +++ directory/apacheds/branches/one-sub-level-index-removal/jdbm/src/main/java/jdbm/recman/FreePhysicalRowIdPageManager.java Fri Sep 2 18:30:57 2011 @@ -87,7 +87,7 @@ final class FreePhysicalRowIdPageManager while ( curs.next() != 0 ) { FreePhysicalRowIdPage fp = FreePhysicalRowIdPage - .getFreePhysicalRowIdPageView( recordFile.get( curs.getCurrent() ) ); + .getFreePhysicalRowIdPageView( recordFile.get( curs.getBlockId() ) ); int slot = fp.getFirstLargerThan( size ); if ( slot != -1 ) @@ -99,12 +99,12 @@ final class FreePhysicalRowIdPageManager if ( fp.getCount() == 0 ) { // page became empty - free it - recordFile.release( curs.getCurrent(), false ); - pageManager.free( Magic.FREEPHYSIDS_PAGE, curs.getCurrent() ); + recordFile.release( curs.getBlockId(), false ); + pageManager.free( Magic.FREEPHYSIDS_PAGE, curs.getBlockId() ); } else { - recordFile.release( curs.getCurrent(), true ); + recordFile.release( curs.getBlockId(), true ); } return retval; @@ -112,7 +112,7 @@ final class FreePhysicalRowIdPageManager else { // no luck, go to next page - recordFile.release( curs.getCurrent(), false ); + recordFile.release( curs.getBlockId(), false ); } } return null; @@ -130,7 +130,7 @@ final class FreePhysicalRowIdPageManager while ( curs.next() != 0 ) { - freePage = curs.getCurrent(); + freePage = curs.getBlockId(); BlockIo curBlock = recordFile.get( freePage ); FreePhysicalRowIdPage fp = FreePhysicalRowIdPage.getFreePhysicalRowIdPageView( curBlock ); int slot = fp.getFirstFree(); Modified: directory/apacheds/branches/one-sub-level-index-removal/jdbm/src/main/java/jdbm/recman/Location.java URL: http://svn.apache.org/viewvc/directory/apacheds/branches/one-sub-level-index-removal/jdbm/src/main/java/jdbm/recman/Location.java?rev=1164667&r1=1164666&r2=1164667&view=diff ============================================================================== --- directory/apacheds/branches/one-sub-level-index-removal/jdbm/src/main/java/jdbm/recman/Location.java (original) +++ directory/apacheds/branches/one-sub-level-index-removal/jdbm/src/main/java/jdbm/recman/Location.java Fri Sep 2 18:30:57 2011 @@ -54,16 +54,22 @@ package jdbm.recman; */ final class Location { - private long block; + /** The block in which the data is stored */ + private long blockId; + + /** The offset within this block */ private short offset; /** * Creates a location from a (block, offset) tuple. + * + * @param blockId The block identifier + * @param offset the offset in the block */ - Location( long block, short offset ) + Location( long blockId, short offset ) { - this.block = block; + this.blockId = blockId; this.offset = offset; } @@ -71,30 +77,34 @@ final class Location /** * Creates a location from a combined block/offset long, as used in the * external representation of logical rowids. A recid is a logical rowid. + * + * @param blockOffset The block + offset combinaison */ Location( long blockOffset ) { this.offset = ( short ) ( blockOffset & 0xffff ); - this.block = blockOffset >> 16; + this.blockId = blockOffset >> 16; } /** * Creates a location based on the data of the physical rowid. + * + * @param physicalRowId The physical row id used as a base for the Location creation */ - Location( PhysicalRowId src ) + Location( PhysicalRowId physicalRowId ) { - block = src.getBlock(); - offset = src.getOffset(); + blockId = physicalRowId.getBlock(); + offset = physicalRowId.getOffset(); } /** - * Returns the file block of the location + * @eturn the blockId of the location */ long getBlock() { - return block; + return blockId; } @@ -114,7 +124,7 @@ final class Location */ long toLong() { - return ( block << 16 ) + ( long ) offset; + return ( blockId << 16 ) + ( long ) offset; } @@ -133,21 +143,26 @@ final class Location @Override public boolean equals( Object o ) { - if ( o == null || ! ( o instanceof Location ) ) + if ( ( o == null ) || ! ( o instanceof Location ) ) { return false; } Location ol = ( Location ) o; - return ol.block == block && ol.offset == offset; + + return ( ol.blockId == blockId ) && ( ol.offset == offset ); } + /** + * {@inheritDoc} + */ public String toString() { StringBuilder sb = new StringBuilder(); - sb.append( "Location ( " ).append( block ).append( " : " ); + sb.append( "Location ( " ).append( blockId ).append( " : " ); sb.append( offset ).append( " ) " ); + return sb.toString(); } } Modified: directory/apacheds/branches/one-sub-level-index-removal/jdbm/src/main/java/jdbm/recman/LogicalRowIdManager.java URL: http://svn.apache.org/viewvc/directory/apacheds/branches/one-sub-level-index-removal/jdbm/src/main/java/jdbm/recman/LogicalRowIdManager.java?rev=1164667&r1=1164666&r2=1164667&view=diff ============================================================================== --- directory/apacheds/branches/one-sub-level-index-removal/jdbm/src/main/java/jdbm/recman/LogicalRowIdManager.java (original) +++ directory/apacheds/branches/one-sub-level-index-removal/jdbm/src/main/java/jdbm/recman/LogicalRowIdManager.java Fri Sep 2 18:30:57 2011 @@ -142,8 +142,8 @@ final class LogicalRowIdManager */ Location fetch( Location rowid ) throws IOException { - TranslationPage xlatPage = TranslationPage.getTranslationPageView( recordFile.get( rowid.getBlock() ) ); + try { Location retval = new Location( xlatPage.get( rowid.getOffset() ) ); Modified: directory/apacheds/branches/one-sub-level-index-removal/jdbm/src/main/java/jdbm/recman/PageCursor.java URL: http://svn.apache.org/viewvc/directory/apacheds/branches/one-sub-level-index-removal/jdbm/src/main/java/jdbm/recman/PageCursor.java?rev=1164667&r1=1164666&r2=1164667&view=diff ============================================================================== --- directory/apacheds/branches/one-sub-level-index-removal/jdbm/src/main/java/jdbm/recman/PageCursor.java (original) +++ directory/apacheds/branches/one-sub-level-index-removal/jdbm/src/main/java/jdbm/recman/PageCursor.java Fri Sep 2 18:30:57 2011 @@ -55,65 +55,85 @@ import java.io.IOException; */ final class PageCursor { - PageManager pageman; - long current; + /** The PageManager */ + PageManager pageManager; + + /** The current block ID */ + long blockId; + + /** The page type */ short type; /** * Constructs a page cursor that starts at the indicated block. + * + * @param pageManager The PageManager */ - PageCursor( PageManager pageman, long current ) + PageCursor( PageManager pageManager, long blockId ) { - this.pageman = pageman; - this.current = current; + this.pageManager = pageManager; + this.blockId = blockId; } /** * Constructs a page cursor that starts at the first block of the * indicated list. + * + * @param pageManager The PageManager + * @param type The page type */ - PageCursor( PageManager pageman, short type ) throws IOException + PageCursor( PageManager pageManager, short type ) throws IOException { - this.pageman = pageman; + this.pageManager = pageManager; this.type = type; } /** - * Returns the current value of the cursor. + * @return the BlockId */ - long getCurrent() throws IOException + long getBlockId() throws IOException { - return current; + return blockId; } /** - * Returns the next value of the cursor. + * @return the next blockId */ long next() throws IOException { - if ( current == 0 ) + if ( blockId == 0 ) { - current = pageman.getFirst( type ); + blockId = pageManager.getFirst( type ); } else { - current = pageman.getNext( current ); + blockId = pageManager.getNext( blockId ); } - return current; + return blockId; } /** - * Returns the previous value of the cursor + * @return the previous blockId */ long prev() throws IOException { - current = pageman.getPrev( current ); - return current; + blockId = pageManager.getPrev( blockId ); + + return blockId; + } + + + /** + * {@inheritDoc} + */ + public String toString() + { + return "Location( " + blockId + ", " + type + ")"; } }