directory-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Pierre-Arnaud Marcelot ...@marcelot.net>
Subject Re: svn commit: r1161416 - in /directory/apacheds/branches/apacheds-jdbm/jdbm/src/main/java/jdbm: btree/BPage.java btree/BTree.java helper/TupleBrowser.java recman/BaseRecordManager.java
Date Thu, 25 Aug 2011 13:15:11 GMT
OK.

Then, it's not only me... :)

Maybe I can still commit this as a working base (and/or maybe those failing test are false positive).
WDYT?

Regards,
Pierre-Arnaud


On 25 août 2011, at 15:10, Emmanuel Lecharny wrote:

> oops,
> 
> 
> so I guess we have to go a step further with the patch. It might work for JDBM tests (but as we only have a few of the base JDBM tests, it may be giving the false feeling it's ok)
> 
> On Thu, Aug 25, 2011 at 3:09 PM, Emmanuel Lecharny <elecharny@apache.org> wrote:
> Same errors.
> 
> I guess we have to go a
> 
> On Thu, Aug 25, 2011 at 2:55 PM, Pierre-Arnaud Marcelot <pa@marcelot.net> wrote:
> I'm getting the following test failures in jdbm-partition:
>> [INFO] ------------------------------------------------------------------------
>> [INFO] Building ApacheDS JDBM Partition 2.0.0-M3-SNAPSHOT
>> [INFO] ------------------------------------------------------------------------
>> [INFO] 
>> [INFO] --- maven-clean-plugin:2.4.1:clean (default-clean) @ apacheds-jdbm-partition ---
>> [INFO] Deleting /Users/pajbam/Development/Apache/apacheds-jdbm/jdbm-partition/target
>> [INFO] 
>> [INFO] --- maven-remote-resources-plugin:1.2:process (default) @ apacheds-jdbm-partition ---
>> [INFO] 
>> [INFO] --- maven-resources-plugin:2.5:resources (default-resources) @ apacheds-jdbm-partition ---
>> [debug] execute contextualize
>> [INFO] Using 'UTF-8' encoding to copy filtered resources.
>> [INFO] skip non existing resourceDirectory /Users/pajbam/Development/Apache/apacheds-jdbm/jdbm-partition/src/main/resources
>> [INFO] Copying 3 resources
>> [INFO] 
>> [INFO] --- maven-compiler-plugin:2.3.2:compile (default-compile) @ apacheds-jdbm-partition ---
>> [INFO] Compiling 20 source files to /Users/pajbam/Development/Apache/apacheds-jdbm/jdbm-partition/target/classes
>> [INFO] 
>> [INFO] --- maven-resources-plugin:2.5:testResources (default-testResources) @ apacheds-jdbm-partition ---
>> [debug] execute contextualize
>> [INFO] Using 'UTF-8' encoding to copy filtered resources.
>> [INFO] Copying 1 resource
>> [INFO] Copying 3 resources
>> [INFO] 
>> [INFO] --- maven-compiler-plugin:2.3.2:testCompile (default-testCompile) @ apacheds-jdbm-partition ---
>> [INFO] Compiling 20 source files to /Users/pajbam/Development/Apache/apacheds-jdbm/jdbm-partition/target/test-classes
>> [INFO] 
>> [INFO] --- maven-surefire-plugin:2.8:test (default-test) @ apacheds-jdbm-partition ---
>> [INFO] Surefire report directory: /Users/pajbam/Development/Apache/apacheds-jdbm/jdbm-partition/target/surefire-reports
>> 
>> -------------------------------------------------------
>>  T E S T S
>> -------------------------------------------------------
>> Running org.apache.directory.server.core.partition.impl.btree.jdbm.BTreeRedirectMarshallerTest
>> Tests run: 7, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.211 sec
>> Running org.apache.directory.server.core.partition.impl.btree.jdbm.DupsContainerCursorTest
>> Tests run: 5, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 2.526 sec <<< FAILURE!
>> Running org.apache.directory.server.core.partition.impl.btree.jdbm.DupsCursorTest
>> Tests run: 15, Failures: 0, Errors: 12, Skipped: 0, Time elapsed: 1.4 sec <<< FAILURE!
>> Running org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmBrowserBugTest
>> Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.024 sec
>> Running org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmIndexTest
>> Tests run: 15, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.865 sec
>> Running org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmMasterTableTest
>> Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.553 sec
>> Running org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmRdnIndexTest
>> Tests run: 8, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.583 sec
>> Running org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmStoreTest
>> Tests run: 18, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 4.443 sec
>> Running org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmTableNoDuplicatesTest
>> Tests run: 8, Failures: 0, Errors: 6, Skipped: 0, Time elapsed: 1.135 sec <<< FAILURE!
>> Running org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmTableWithDuplicatesTest
>> Tests run: 15, Failures: 0, Errors: 9, Skipped: 0, Time elapsed: 0.918 sec <<< FAILURE!
>> Running org.apache.directory.server.core.partition.impl.btree.jdbm.KeyBTreeCursorTest
>> Tests run: 5, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.071 sec
>> Running org.apache.directory.server.core.partition.impl.btree.jdbm.KeyCursorTest
>> Tests run: 5, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.06 sec
>> Running org.apache.directory.server.core.partition.impl.btree.jdbm.KeyTupleArrayCursorTest
>> Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.005 sec
>> Running org.apache.directory.server.core.partition.impl.btree.jdbm.KeyTupleBTreeCursorTest
>> Tests run: 2, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 0.558 sec <<< FAILURE!
>> Running org.apache.directory.server.core.partition.impl.btree.jdbm.NoDupsCursorTest
>> Tests run: 5, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 0.491 sec <<< FAILURE!
>> Running org.apache.directory.server.core.partition.impl.btree.jdbm.ServerEntrySerializerTest
>> Tests run: 9, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.466 sec
>> Running org.apache.directory.server.core.partition.impl.btree.jdbm.StringSerializerTest
>> Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.058 sec
>> Running org.apache.directory.server.core.partition.tree.PartitionTreeTest
>> Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.454 sec
>> Running org.apache.directory.server.core.schema.PartitionSchemaLoaderTest
>> Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.377 sec
>> 
>> Results :
>> 
>> Tests in error: 
>>   testOnTableWithMultipleEntries(org.apache.directory.server.core.partition.impl.btree.jdbm.DupsContainerCursorTest)
>>   testNextNoDups(org.apache.directory.server.core.partition.impl.btree.jdbm.DupsCursorTest)
>>   testPreviousNoDups(org.apache.directory.server.core.partition.impl.btree.jdbm.DupsCursorTest)
>>   testNextDups(org.apache.directory.server.core.partition.impl.btree.jdbm.DupsCursorTest)
>>   testPreviousDups(org.apache.directory.server.core.partition.impl.btree.jdbm.DupsCursorTest)
>>   testFirstLastUnderDupLimit(org.apache.directory.server.core.partition.impl.btree.jdbm.DupsCursorTest)
>>   testFirstLastOverDupLimit(org.apache.directory.server.core.partition.impl.btree.jdbm.DupsCursorTest)
>>   testFirstOverDupLimit(org.apache.directory.server.core.partition.impl.btree.jdbm.DupsCursorTest)
>>   testLastOverDupLimit(org.apache.directory.server.core.partition.impl.btree.jdbm.DupsCursorTest)
>>   testOverDupLimit(org.apache.directory.server.core.partition.impl.btree.jdbm.DupsCursorTest)
>>   testUnderDupLimit(org.apache.directory.server.core.partition.impl.btree.jdbm.DupsCursorTest)
>>   testBeforeAfterBelowDupLimit(org.apache.directory.server.core.partition.impl.btree.jdbm.DupsCursorTest)
>>   testBeforeAfterOverDupLimit(org.apache.directory.server.core.partition.impl.btree.jdbm.DupsCursorTest)
>>   testCloseReopen(org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmTableNoDuplicatesTest)
>>   testLoadData(org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmTableNoDuplicatesTest)
>>   testNullOrEmptyKeyValue(org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmTableNoDuplicatesTest)
>>   testRemove(org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmTableNoDuplicatesTest)
>>   testPut(org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmTableNoDuplicatesTest)
>>   testHas(org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmTableNoDuplicatesTest)
>>   testMiscellaneous(org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmTableWithDuplicatesTest)
>>   testCloseReopen(org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmTableWithDuplicatesTest)
>>   testLoadData(org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmTableWithDuplicatesTest)
>>   testNullOrEmptyKeyValue(org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmTableWithDuplicatesTest)
>>   testRemove(org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmTableWithDuplicatesTest)
>>   testPut(org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmTableWithDuplicatesTest)
>>   testHas(org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmTableWithDuplicatesTest)
>>   testDuplicateLimit(org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmTableWithDuplicatesTest)
>>   testNullOrEmptyKeyValueAfterDuplicateLimit(org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmTableWithDuplicatesTest)
>>   testNonEmptyCursor(org.apache.directory.server.core.partition.impl.btree.jdbm.KeyTupleBTreeCursorTest)
>>   testOnTableWithMultipleEntries(org.apache.directory.server.core.partition.impl.btree.jdbm.NoDupsCursorTest)
>> 
>> Tests run: 127, Failures: 0, Errors: 30, Skipped: 0
> 
> 
> Did I miss something?!?
> Is there another patch to apply, other than the 'jdbm1.diff' file?
> 
> Regards,
> Pierre-Arnaud
> 
> On 25 août 2011, at 14:49, Pierre-Arnaud Marcelot wrote:
> 
>> OK,
>> 
>> I moved the two test classes to their dedicated package 'jdbm.helper'.
>> 
>> And fixed the compilation failure in Maven (strangely it was fine in Eclipse) by replacing this in:
>>> lrus = (LRUCache<K,V>.LRU [] )new LRUCache.LRU[NUM_LRUS];
>> 
>> by this:
>>> lrus = ( LRUCache.LRU[] ) new LRUCache.LRU[NUM_LRUS];
>> 
>> Compilation is now fine in Eclipse *and* Maven.
>> 
>> Now, running all tests.
>> 
>> Regards,
>> Pierre-Arnaud
>> 
>> 
>> On 25 août 2011, at 14:42, Pierre-Arnaud Marcelot wrote:
>> 
>>> I'm having trouble applying the 'jdbm1.diff' patch.
>>> 
>>> Patching went fine, however there are two test classes that are not in the correct package, TestActionVersioning and TestVersionedCache, and Maven is reporting a compilation failure:
>>>> [ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:2.3.2:compile (default-compile) on project apacheds-jdbm: Compilation failure: Compilation failure:
>>>> [ERROR] /Users/pajbam/Development/Apache/apacheds-jdbm/jdbm/src/main/java/jdbm/helper/LRUCache.java:[118,28] ')' expected
>>>> [ERROR] /Users/pajbam/Development/Apache/apacheds-jdbm/jdbm/src/main/java/jdbm/helper/LRUCache.java:[118,29] ';' expected
>>>> [ERROR] /Users/pajbam/Development/Apache/apacheds-jdbm/jdbm/src/main/java/jdbm/helper/LRUCache.java:[118,33] illegal start of expression
>>>> [ERROR] /Users/pajbam/Development/Apache/apacheds-jdbm/jdbm/src/main/java/jdbm/helper/LRUCache.java:[118,34] ';' expected
>>>> [ERROR] /Users/pajbam/Development/Apache/apacheds-jdbm/jdbm/src/main/java/jdbm/helper/LRUCache.java:[118,36] illegal start of expression
>>>> [ERROR] /Users/pajbam/Development/Apache/apacheds-jdbm/jdbm/src/main/java/jdbm/helper/LRUCache.java:[118,37] ';' expected
>>>> [ERROR] /Users/pajbam/Development/Apache/apacheds-jdbm/jdbm/src/main/java/jdbm/helper/LRUCache.java:[118,54] not a statement
>>> 
>>> 
>>> More to come...
>>> 
>>> Regards,
>>> Pierre-Arnaud
>>> 
>>> On 25 août 2011, at 13:58, Pierre-Arnaud Marcelot wrote:
>>> 
>>>> Sure, I'm on it.
>>>> 
>>>> Regards,
>>>> Pierre-Arnaud
>>>> 
>>>> On 25 août 2011, at 13:41, Emmanuel Lecharny wrote:
>>>> 
>>>>> Strange... I did a pacth -p0 with your diff.
>>>>> 
>>>>> I may have not svn added the files.
>>>>> 
>>>>> i'm not connected right now (using a client internet connection, will do that tonite
>>>>> 
>>>>> Or maybe Pierre-Arnaud can apply the patch, do a svn add of the added files and commit ?
>>>>> 
>>>>> On Thu, Aug 25, 2011 at 9:41 AM, Selcuk AYA <ayaselcuk@gmail.com> wrote:
>>>>> Hi,
>>>>> there are alos a bunch of new filesn the patch I provided. Can you
>>>>> also add them to the branch?
>>>>> 
>>>>> A    jdbm/src/test/java/jdbm/helper
>>>>> A    jdbm/src/test/java/jdbm/btree/SnapshotBTree.java
>>>>> A    jdbm/src/test/java/jdbm/TestActionVersioning.java
>>>>> A    jdbm/src/test/java/jdbm/TestVersionedCache.java
>>>>> A    jdbm/src/main/java/jdbm/helper/ExplicitList.java
>>>>> A    jdbm/src/main/java/jdbm/helper/ActionVersioning.java
>>>>> A    jdbm/src/main/java/jdbm/helper/ActionContext.java
>>>>> A    jdbm/src/main/java/jdbm/helper/LRUCache.java
>>>>> A    jdbm/src/main/java/jdbm/helper/EntryIO.java
>>>>> A    jdbm/src/main/java/jdbm/recman/SnapshotRecordManager.java
>>>>> A    jdbm/src/main/java/jdbm/ActionRecordManager.java
>>>>> 
>>>>> regards,
>>>>> Selcuk
>>>>> 
>>>>> On Thu, Aug 25, 2011 at 9:58 AM,  <elecharny@apache.org> wrote:
>>>>> > Author: elecharny
>>>>> > Date: Thu Aug 25 06:58:36 2011
>>>>> > New Revision: 1161416
>>>>> >
>>>>> > URL: http://svn.apache.org/viewvc?rev=1161416&view=rev
>>>>> > Log:
>>>>> > applied Selcuk's patch
>>>>> >
>>>>> > Modified:
>>>>> >    directory/apacheds/branches/apacheds-jdbm/jdbm/src/main/java/jdbm/btree/BPage.java
>>>>> >    directory/apacheds/branches/apacheds-jdbm/jdbm/src/main/java/jdbm/btree/BTree.java
>>>>> >    directory/apacheds/branches/apacheds-jdbm/jdbm/src/main/java/jdbm/helper/TupleBrowser.java
>>>>> >    directory/apacheds/branches/apacheds-jdbm/jdbm/src/main/java/jdbm/recman/BaseRecordManager.java
>>>>> >
>>>>> > Modified: directory/apacheds/branches/apacheds-jdbm/jdbm/src/main/java/jdbm/btree/BPage.java
>>>>> > URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-jdbm/jdbm/src/main/java/jdbm/btree/BPage.java?rev=1161416&r1=1161415&r2=1161416&view=diff
>>>>> > ==============================================================================
>>>>> > --- directory/apacheds/branches/apacheds-jdbm/jdbm/src/main/java/jdbm/btree/BPage.java (original)
>>>>> > +++ directory/apacheds/branches/apacheds-jdbm/jdbm/src/main/java/jdbm/btree/BPage.java Thu Aug 25 06:58:36 2011
>>>>> > @@ -50,6 +50,7 @@ package jdbm.btree;
>>>>> >  import jdbm.helper.Serializer;
>>>>> >  import jdbm.helper.Tuple;
>>>>> >  import jdbm.helper.TupleBrowser;
>>>>> > +import jdbm.helper.ActionContext;
>>>>> >
>>>>> >  import java.io.IOException;
>>>>> >  import java.io.ByteArrayOutputStream;
>>>>> > @@ -259,10 +260,11 @@ public class BPage<K, V> implements Seri
>>>>> >      *
>>>>> >      * @param height Height of the current BPage (zero is leaf page)
>>>>> >      * @param key The key
>>>>> > +     * @param context action specific context. not null if record manager is action capable
>>>>> >      * @return TupleBrowser positionned just before the given key, or before
>>>>> >      *                      next greater key if key isn't found.
>>>>> >      */
>>>>> > -    TupleBrowser<K, V> find( int height, K key ) throws IOException
>>>>> > +    TupleBrowser<K, V> find( int height, K key, ActionContext context ) throws IOException
>>>>> >     {
>>>>> >         int index = this.findChildren( key );
>>>>> >
>>>>> > @@ -285,26 +287,27 @@ public class BPage<K, V> implements Seri
>>>>> >             }
>>>>> >         }
>>>>> >
>>>>> > -        return new Browser( child, index );
>>>>> > +        return new Browser( child, index, context );
>>>>> >     }
>>>>> >
>>>>> >
>>>>> >     /**
>>>>> >      * Find first entry and return a browser positioned before it.
>>>>> >      *
>>>>> > +     * @param context action specific context. not null if record manager is action capable
>>>>> >      * @return TupleBrowser positionned just before the first entry.
>>>>> >      */
>>>>> > -    TupleBrowser<K, V> findFirst() throws IOException
>>>>> > +    TupleBrowser<K, V> findFirst(ActionContext context ) throws IOException
>>>>> >     {
>>>>> >         if ( isLeaf )
>>>>> >         {
>>>>> > -            return new Browser( this, first );
>>>>> > +            return new Browser( this, first, context );
>>>>> >         }
>>>>> >         else
>>>>> >         {
>>>>> >             BPage<K, V> child = childBPage( first );
>>>>> >
>>>>> > -            return child.findFirst();
>>>>> > +            return child.findFirst( context );
>>>>> >         }
>>>>> >     }
>>>>> >
>>>>> > @@ -337,9 +340,12 @@ public class BPage<K, V> implements Seri
>>>>> >
>>>>> >         height -= 1;
>>>>> >
>>>>> > +        BPage<K,V> pageNewCopy = null;
>>>>> >         if ( height == 0 )
>>>>> >         {
>>>>> > +            pageNewCopy = btree.copyOnWrite( this );
>>>>> >             result = new InsertResult<K, V>();
>>>>> > +            result.pageNewCopy = pageNewCopy;
>>>>> >
>>>>> >             // inserting on a leaf BPage
>>>>> >             overflow = -1;
>>>>> > @@ -365,8 +371,8 @@ public class BPage<K, V> implements Seri
>>>>> >
>>>>> >                 if ( replace )
>>>>> >                 {
>>>>> > -                    values[index] = value;
>>>>> > -                    btree.recordManager.update( recordId, this, this );
>>>>> > +                    pageNewCopy.values[index] = value;
>>>>> > +                    btree.recordManager.update( recordId, pageNewCopy, this );
>>>>> >                 }
>>>>> >
>>>>> >                 // return the existing key
>>>>> > @@ -378,6 +384,11 @@ public class BPage<K, V> implements Seri
>>>>> >             // non-leaf BPage
>>>>> >             BPage<K, V> child = childBPage( index );
>>>>> >             result = child.insert( height, key, value, replace );
>>>>> > +            if( result.pageNewCopy != null)
>>>>> > +            {
>>>>> > +                child = result.pageNewCopy;
>>>>> > +                result.pageNewCopy = null;
>>>>> > +            }
>>>>> >
>>>>> >             if ( result.existing != null )
>>>>> >             {
>>>>> > @@ -393,6 +404,9 @@ public class BPage<K, V> implements Seri
>>>>> >
>>>>> >             // there was an overflow, we need to insert the overflow page
>>>>> >             // on this BPage
>>>>> > +            pageNewCopy = btree.copyOnWrite( this );
>>>>> > +            result.pageNewCopy = pageNewCopy;
>>>>> > +
>>>>> >             if ( DEBUG )
>>>>> >             {
>>>>> >                 System.out.println( "BPage.insert() Overflow page: " + result.overflow.recordId );
>>>>> > @@ -402,7 +416,7 @@ public class BPage<K, V> implements Seri
>>>>> >             overflow = result.overflow.recordId;
>>>>> >
>>>>> >             // update child's largest key
>>>>> > -            keys[index] = child.getLargestKey();
>>>>> > +            pageNewCopy.keys[index] = child.getLargestKey();
>>>>> >
>>>>> >             // clean result so we can reuse it
>>>>> >             result.overflow = null;
>>>>> > @@ -410,24 +424,25 @@ public class BPage<K, V> implements Seri
>>>>> >
>>>>> >         // if we get here, we need to insert a new entry on the BPage
>>>>> >         // before children[ index ]
>>>>> > -        if ( !isFull() )
>>>>> > -        {
>>>>> > +        if ( !pageNewCopy.isFull() )
>>>>> > +        {
>>>>> >             if ( height == 0 )
>>>>> >             {
>>>>> > -                insertEntry( this, index - 1, key, value );
>>>>> > +                insertEntry( pageNewCopy, index - 1, key, value );
>>>>> >             }
>>>>> >             else
>>>>> >             {
>>>>> > -                insertChild( this, index - 1, key, overflow );
>>>>> > +                insertChild( pageNewCopy, index - 1, key, overflow );
>>>>> >             }
>>>>> >
>>>>> > -            btree.recordManager.update( recordId, this, this );
>>>>> > +            btree.recordManager.update( recordId, pageNewCopy, this );
>>>>> >             return result;
>>>>> >         }
>>>>> >
>>>>> >         // page is full, we must divide the page
>>>>> >         int half = btree.pageSize >> 1;
>>>>> > -        BPage<K, V> newPage = new BPage<K, V>( btree, isLeaf );
>>>>> > +        BPage<K, V> newPage = new BPage<K, V>( btree, pageNewCopy.isLeaf );
>>>>> > +
>>>>> >
>>>>> >         if ( index < half )
>>>>> >         {
>>>>> > @@ -441,15 +456,15 @@ public class BPage<K, V> implements Seri
>>>>> >
>>>>> >             if ( height == 0 )
>>>>> >             {
>>>>> > -                copyEntries( this, 0, newPage, half, index );
>>>>> > +                copyEntries( pageNewCopy, 0, newPage, half, index );
>>>>> >                 setEntry( newPage, half + index, key, value );
>>>>> > -                copyEntries( this, index, newPage, half + index + 1, half - index - 1 );
>>>>> > +                copyEntries( pageNewCopy, index, newPage, half + index + 1, half - index - 1 );
>>>>> >             }
>>>>> >             else
>>>>> >             {
>>>>> > -                copyChildren( this, 0, newPage, half, index );
>>>>> > +                copyChildren( pageNewCopy, 0, newPage, half, index );
>>>>> >                 setChild( newPage, half + index, key, overflow );
>>>>> > -                copyChildren( this, index, newPage, half + index + 1, half - index - 1 );
>>>>> > +                copyChildren( pageNewCopy, index, newPage, half + index + 1, half - index - 1 );
>>>>> >             }
>>>>> >         }
>>>>> >         else
>>>>> > @@ -463,50 +478,51 @@ public class BPage<K, V> implements Seri
>>>>> >
>>>>> >             if ( height == 0 )
>>>>> >             {
>>>>> > -                copyEntries( this, 0, newPage, half, half );
>>>>> > -                copyEntries( this, half, this, half - 1, index - half );
>>>>> > -                setEntry( this, index - 1, key, value );
>>>>> > +                copyEntries( pageNewCopy, 0, newPage, half, half );
>>>>> > +                copyEntries( pageNewCopy, half, pageNewCopy, half - 1, index - half );
>>>>> > +                setEntry( pageNewCopy, index - 1, key, value );
>>>>> >             }
>>>>> >             else
>>>>> >             {
>>>>> > -                copyChildren( this, 0, newPage, half, half );
>>>>> > -                copyChildren( this, half, this, half - 1, index - half );
>>>>> > -                setChild( this, index - 1, key, overflow );
>>>>> > +                copyChildren( pageNewCopy, 0, newPage, half, half );
>>>>> > +                copyChildren( pageNewCopy, half, pageNewCopy, half - 1, index - half );
>>>>> > +                setChild( pageNewCopy, index - 1, key, overflow );
>>>>> >             }
>>>>> >         }
>>>>> >
>>>>> > -        first = half - 1;
>>>>> > +        pageNewCopy.first = half - 1;
>>>>> >
>>>>> >         // nullify lower half of entries
>>>>> > -        for ( int i = 0; i < first; i++ )
>>>>> > +        for ( int i = 0; i < pageNewCopy.first; i++ )
>>>>> >         {
>>>>> >             if ( height == 0 )
>>>>> >             {
>>>>> > -                setEntry( this, i, null, null );
>>>>> > +                setEntry( pageNewCopy, i, null, null );
>>>>> >             }
>>>>> >             else
>>>>> >             {
>>>>> > -                setChild( this, i, null, -1 );
>>>>> > +                setChild( pageNewCopy, i, null, -1 );
>>>>> >             }
>>>>> >         }
>>>>> >
>>>>> > -        if ( isLeaf )
>>>>> > +        if ( pageNewCopy.isLeaf )
>>>>> >         {
>>>>> >             // link newly created BPage
>>>>> > -            newPage.previous = previous;
>>>>> > -            newPage.next = recordId;
>>>>> > +            newPage.previous = pageNewCopy.previous;
>>>>> > +            newPage.next = pageNewCopy.recordId;
>>>>> >
>>>>> > -            if ( previous != 0 )
>>>>> > +            if ( pageNewCopy.previous != 0 )
>>>>> >             {
>>>>> > -                BPage<K, V> previousBPage = loadBPage( previous );
>>>>> > +                BPage<K, V> previousBPage = loadBPage( pageNewCopy.previous );
>>>>> > +                previousBPage = btree.copyOnWrite( previousBPage );
>>>>> >                 previousBPage.next = newPage.recordId;
>>>>> > -                btree.recordManager.update( previous, previousBPage, this );
>>>>> > +                btree.recordManager.update( pageNewCopy.previous, previousBPage, this );
>>>>> >             }
>>>>> >
>>>>> > -            previous = newPage.recordId;
>>>>> > +            pageNewCopy.previous = newPage.recordId;
>>>>> >         }
>>>>> >
>>>>> > -        btree.recordManager.update( recordId, this, this );
>>>>> > +        btree.recordManager.update( recordId, pageNewCopy, this );
>>>>> >         btree.recordManager.update( newPage.recordId, newPage, this );
>>>>> >
>>>>> >         result.overflow = newPage;
>>>>> > @@ -521,9 +537,9 @@ public class BPage<K, V> implements Seri
>>>>> >      * @param key Removal key
>>>>> >      * @return Remove result object
>>>>> >      */
>>>>> > -    RemoveResult<V> remove( int height, K key ) throws IOException
>>>>> > +    RemoveResult<K, V> remove( int height, K key ) throws IOException
>>>>> >     {
>>>>> > -        RemoveResult<V> result;
>>>>> > +        RemoveResult<K, V> result;
>>>>> >
>>>>> >         int half = btree.pageSize / 2;
>>>>> >         int index = findChildren( key );
>>>>> > @@ -536,6 +552,7 @@ public class BPage<K, V> implements Seri
>>>>> >
>>>>> >         height -= 1;
>>>>> >
>>>>> > +        BPage<K,V> pageNewCopy = btree.copyOnWrite( this );;
>>>>> >         if ( height == 0 )
>>>>> >         {
>>>>> >             // remove leaf entry
>>>>> > @@ -544,22 +561,32 @@ public class BPage<K, V> implements Seri
>>>>> >                 throw new IllegalArgumentException( I18n.err( I18n.ERR_514, key ) );
>>>>> >             }
>>>>> >
>>>>> > -            result = new RemoveResult<V>();
>>>>> > -            result.value = values[index];
>>>>> > -            removeEntry( this, index );
>>>>> > +            result = new RemoveResult<K, V>();
>>>>> > +            result.value = pageNewCopy.values[index];
>>>>> > +            removeEntry( pageNewCopy, index );
>>>>> >
>>>>> >             // update this BPage
>>>>> > -            btree.recordManager.update( recordId, this, this );
>>>>> > +            btree.recordManager.update( recordId, pageNewCopy, this );
>>>>> >         }
>>>>> >         else
>>>>> >         {
>>>>> >             // recurse into Btree to remove entry on a children page
>>>>> >             BPage<K, V> child = childBPage( index );
>>>>> >             result = child.remove( height, key );
>>>>> > +
>>>>> > +            if ( result.pageNewCopy != null )
>>>>> > +            {
>>>>> > +                child = result.pageNewCopy;
>>>>> > +                result.pageNewCopy = null;
>>>>> > +            }
>>>>> > +            else
>>>>> > +            {
>>>>> > +                child = btree.copyOnWrite( child );
>>>>> > +            }
>>>>> >
>>>>> >             // update children
>>>>> > -            keys[index] = child.getLargestKey();
>>>>> > -            btree.recordManager.update( recordId, this, this );
>>>>> > +            pageNewCopy.keys[index] = child.getLargestKey();
>>>>> > +            btree.recordManager.update( recordId, pageNewCopy, this );
>>>>> >
>>>>> >             if ( result.underflow )
>>>>> >             {
>>>>> > @@ -569,10 +596,11 @@ public class BPage<K, V> implements Seri
>>>>> >                     throw new IllegalStateException( I18n.err( I18n.ERR_513, "1" ) );
>>>>> >                 }
>>>>> >
>>>>> > -                if ( index < children.length - 1 )
>>>>> > +                if ( index < pageNewCopy.children.length - 1 )
>>>>> >                 {
>>>>> >                     // exists greater brother page
>>>>> > -                    BPage<K, V> brother = childBPage( index + 1 );
>>>>> > +                    BPage<K, V> brother = pageNewCopy.childBPage( index + 1 );
>>>>> > +                    brother = btree.copyOnWrite( brother );
>>>>> >                     int bfirst = brother.first;
>>>>> >
>>>>> >                     if ( bfirst < half )
>>>>> > @@ -606,12 +634,12 @@ public class BPage<K, V> implements Seri
>>>>> >                         }
>>>>> >
>>>>> >                         // update child's largest key
>>>>> > -                        keys[index] = child.getLargestKey();
>>>>> > +                        pageNewCopy.keys[index] = child.getLargestKey();
>>>>> >
>>>>> >                         // no change in previous/next BPage
>>>>> >
>>>>> >                         // update BPages
>>>>> > -                        btree.recordManager.update( recordId, this, this );
>>>>> > +                        btree.recordManager.update( recordId, pageNewCopy, this );
>>>>> >                         btree.recordManager.update( brother.recordId, brother, this );
>>>>> >                         btree.recordManager.update( child.recordId, child, this );
>>>>> >
>>>>> > @@ -638,24 +666,25 @@ public class BPage<K, V> implements Seri
>>>>> >                         btree.recordManager.update( brother.recordId, brother, this );
>>>>> >
>>>>> >                         // remove "child" from current BPage
>>>>> > -                        if ( isLeaf )
>>>>> > +                        if ( pageNewCopy.isLeaf )
>>>>> >                         {
>>>>> > -                            copyEntries( this, first, this, first + 1, index - first );
>>>>> > -                            setEntry( this, first, null, null );
>>>>> > +                            copyEntries( pageNewCopy, pageNewCopy.first, pageNewCopy, pageNewCopy.first + 1, index - pageNewCopy.first );
>>>>> > +                            setEntry( pageNewCopy, pageNewCopy.first, null, null );
>>>>> >                         }
>>>>> >                         else
>>>>> >                         {
>>>>> > -                            copyChildren( this, first, this, first + 1, index - first );
>>>>> > -                            setChild( this, first, null, -1 );
>>>>> > +                            copyChildren( pageNewCopy, pageNewCopy.first, pageNewCopy, pageNewCopy.first + 1, index - pageNewCopy.first );
>>>>> > +                            setChild( pageNewCopy, pageNewCopy.first, null, -1 );
>>>>> >                         }
>>>>> >
>>>>> > -                        first += 1;
>>>>> > -                        btree.recordManager.update( recordId, this, this );
>>>>> > +                        pageNewCopy.first += 1;
>>>>> > +                        btree.recordManager.update( recordId, pageNewCopy, this );
>>>>> >
>>>>> >                         // re-link previous and next BPages
>>>>> >                         if ( child.previous != 0 )
>>>>> >                         {
>>>>> >                             BPage<K, V> prev = loadBPage( child.previous );
>>>>> > +                            prev = btree.copyOnWrite( prev );
>>>>> >                             prev.next = child.next;
>>>>> >                             btree.recordManager.update( prev.recordId, prev, this );
>>>>> >                         }
>>>>> > @@ -663,6 +692,7 @@ public class BPage<K, V> implements Seri
>>>>> >                         if ( child.next != 0 )
>>>>> >                         {
>>>>> >                             BPage<K, V> next = loadBPage( child.next );
>>>>> > +                            next = btree.copyOnWrite( next );
>>>>> >                             next.previous = child.previous;
>>>>> >                             btree.recordManager.update( next.recordId, next, this );
>>>>> >                         }
>>>>> > @@ -674,7 +704,8 @@ public class BPage<K, V> implements Seri
>>>>> >                 else
>>>>> >                 {
>>>>> >                     // page "brother" is before "child"
>>>>> > -                    BPage<K, V> brother = childBPage( index - 1 );
>>>>> > +                    BPage<K, V> brother = pageNewCopy.childBPage( index - 1 );
>>>>> > +                    brother = btree.copyOnWrite( brother );
>>>>> >                     int bfirst = brother.first;
>>>>> >
>>>>> >                     if ( bfirst < half )
>>>>> > @@ -708,12 +739,12 @@ public class BPage<K, V> implements Seri
>>>>> >                         }
>>>>> >
>>>>> >                         // update brother's largest key
>>>>> > -                        keys[index - 1] = brother.getLargestKey();
>>>>> > +                        pageNewCopy.keys[index - 1] = brother.getLargestKey();
>>>>> >
>>>>> >                         // no change in previous/next BPage
>>>>> >
>>>>> >                         // update BPages
>>>>> > -                        btree.recordManager.update( recordId, this, this );
>>>>> > +                        btree.recordManager.update( recordId, pageNewCopy, this );
>>>>> >                         btree.recordManager.update( brother.recordId, brother, this );
>>>>> >                         btree.recordManager.update( child.recordId, child, this );
>>>>> >
>>>>> > @@ -740,24 +771,25 @@ public class BPage<K, V> implements Seri
>>>>> >                         btree.recordManager.update( child.recordId, child, this );
>>>>> >
>>>>> >                         // remove "brother" from current BPage
>>>>> > -                        if ( isLeaf )
>>>>> > +                        if ( pageNewCopy.isLeaf )
>>>>> >                         {
>>>>> > -                            copyEntries( this, first, this, first + 1, index - 1 - first );
>>>>> > -                            setEntry( this, first, null, null );
>>>>> > +                            copyEntries( pageNewCopy, pageNewCopy.first, pageNewCopy, pageNewCopy.first + 1, index - 1 - pageNewCopy.first );
>>>>> > +                            setEntry( pageNewCopy, pageNewCopy.first, null, null );
>>>>> >                         }
>>>>> >                         else
>>>>> >                         {
>>>>> > -                            copyChildren( this, first, this, first + 1, index - 1 - first );
>>>>> > -                            setChild( this, first, null, -1 );
>>>>> > +                            copyChildren( pageNewCopy, pageNewCopy.first, pageNewCopy, pageNewCopy.first + 1, index - 1 - pageNewCopy.first );
>>>>> > +                            setChild( pageNewCopy, pageNewCopy.first, null, -1 );
>>>>> >                         }
>>>>> >
>>>>> > -                        first += 1;
>>>>> > -                        btree.recordManager.update( recordId, this, this );
>>>>> > +                        pageNewCopy.first += 1;
>>>>> > +                        btree.recordManager.update( recordId, pageNewCopy, this );
>>>>> >
>>>>> >                         // re-link previous and next BPages
>>>>> >                         if ( brother.previous != 0 )
>>>>> >                         {
>>>>> >                             BPage<K, V> prev = loadBPage( brother.previous );
>>>>> > +                            prev = btree.copyOnWrite( prev );
>>>>> >                             prev.next = brother.next;
>>>>> >                             btree.recordManager.update( prev.recordId, prev, this );
>>>>> >                         }
>>>>> > @@ -765,6 +797,7 @@ public class BPage<K, V> implements Seri
>>>>> >                         if ( brother.next != 0 )
>>>>> >                         {
>>>>> >                             BPage<K, V> next = loadBPage( brother.next );
>>>>> > +                            next = btree.copyOnWrite( next );
>>>>> >                             next.previous = brother.previous;
>>>>> >                             btree.recordManager.update( next.recordId, next, this );
>>>>> >                         }
>>>>> > @@ -777,7 +810,8 @@ public class BPage<K, V> implements Seri
>>>>> >         }
>>>>> >
>>>>> >         // underflow if page is more than half-empty
>>>>> > -        result.underflow = first > half;
>>>>> > +        result.underflow = pageNewCopy.first > half;
>>>>> > +        result.pageNewCopy = pageNewCopy;
>>>>> >
>>>>> >         return result;
>>>>> >     }
>>>>> > @@ -1316,13 +1350,18 @@ public class BPage<K, V> implements Seri
>>>>> >          * Existing value for the insertion key.
>>>>> >          */
>>>>> >         V existing;
>>>>> > +
>>>>> > +        /**
>>>>> > +         * New version of the page doing the insert
>>>>> > +         */
>>>>> > +        BPage<K, V> pageNewCopy;
>>>>> >     }
>>>>> >
>>>>> >     /** STATIC INNER CLASS
>>>>> >      *  Result from remove() method call. If we had to removed a BPage,
>>>>> >      *  it will be stored into the underflow field.
>>>>> >      */
>>>>> > -    static class RemoveResult<V>
>>>>> > +    static class RemoveResult<K, V>
>>>>> >     {
>>>>> >         /**
>>>>> >          * Set to true if underlying pages underflowed
>>>>> > @@ -1333,6 +1372,12 @@ public class BPage<K, V> implements Seri
>>>>> >          * Removed entry value
>>>>> >          */
>>>>> >         V value;
>>>>> > +
>>>>> > +        /**
>>>>> > +         * New version of the page doing the remove
>>>>> > +         */
>>>>> > +        BPage<K, V> pageNewCopy;
>>>>> > +
>>>>> >     }
>>>>> >
>>>>> >     /** PRIVATE INNER CLASS
>>>>> > @@ -1342,6 +1387,9 @@ public class BPage<K, V> implements Seri
>>>>> >     {
>>>>> >         /** Current page. */
>>>>> >         private BPage<K, V> page;
>>>>> > +
>>>>> > +        /** Browsing action's context in case of a action capable record manager */
>>>>> > +        ActionContext context;
>>>>> >
>>>>> >         /**
>>>>> >          * Current index in the page.  The index positionned on the next
>>>>> > @@ -1354,12 +1402,14 @@ public class BPage<K, V> implements Seri
>>>>> >          * Create a browser.
>>>>> >          *
>>>>> >          * @param page Current page
>>>>> > +         * @param context Action specific context. Not null if part of an action
>>>>> >          * @param index Position of the next tuple to return.
>>>>> >          */
>>>>> > -        Browser( BPage<K, V> page, int index )
>>>>> > +        Browser( BPage<K, V> page, int index, ActionContext context )
>>>>> >         {
>>>>> >             this.page = page;
>>>>> >             this.index = index;
>>>>> > +            this.context = context;
>>>>> >         }
>>>>> >
>>>>> >
>>>>> > @@ -1373,6 +1423,7 @@ public class BPage<K, V> implements Seri
>>>>> >          */
>>>>> >         public boolean getNext( Tuple<K, V> tuple ) throws IOException
>>>>> >         {
>>>>> > +            btree.setAsCurrentAction( context );
>>>>> >             // First, check that we are within a page
>>>>> >             if ( index < page.btree.pageSize )
>>>>> >             {
>>>>> > @@ -1402,6 +1453,7 @@ public class BPage<K, V> implements Seri
>>>>> >
>>>>> >         public boolean getPrevious( Tuple<K, V> tuple ) throws IOException
>>>>> >         {
>>>>> > +            btree.setAsCurrentAction( context );
>>>>> >             if ( index == page.first )
>>>>> >             {
>>>>> >                 if ( page.previous != 0 )
>>>>> > @@ -1422,6 +1474,12 @@ public class BPage<K, V> implements Seri
>>>>> >
>>>>> >             return true;
>>>>> >         }
>>>>> > +
>>>>> > +        @Override
>>>>> > +        public void close()
>>>>> > +        {
>>>>> > +            btree.endAction( context );
>>>>> > +        }
>>>>> >     }
>>>>> >
>>>>> >
>>>>> >
>>>>> > Modified: directory/apacheds/branches/apacheds-jdbm/jdbm/src/main/java/jdbm/btree/BTree.java
>>>>> > URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-jdbm/jdbm/src/main/java/jdbm/btree/BTree.java?rev=1161416&r1=1161415&r2=1161416&view=diff
>>>>> > ==============================================================================
>>>>> > --- directory/apacheds/branches/apacheds-jdbm/jdbm/src/main/java/jdbm/btree/BTree.java (original)
>>>>> > +++ directory/apacheds/branches/apacheds-jdbm/jdbm/src/main/java/jdbm/btree/BTree.java Thu Aug 25 06:58:36 2011
>>>>> > @@ -52,13 +52,23 @@ import java.io.IOException;
>>>>> >  import java.io.ObjectInput;
>>>>> >  import java.io.ObjectOutput;
>>>>> >  import java.io.Serializable;
>>>>> > +import java.io.ByteArrayInputStream;
>>>>> > +import java.io.ByteArrayOutputStream;
>>>>> > +import java.io.ObjectInputStream;
>>>>> > +import java.io.ObjectOutputStream;
>>>>> >  import java.util.Comparator;
>>>>> >  import java.util.concurrent.atomic.AtomicInteger;
>>>>> >
>>>>> >  import jdbm.RecordManager;
>>>>> > +import jdbm.ActionRecordManager;
>>>>> >  import jdbm.helper.Serializer;
>>>>> >  import jdbm.helper.Tuple;
>>>>> >  import jdbm.helper.TupleBrowser;
>>>>> > +import jdbm.helper.WrappedRuntimeException;
>>>>> > +import jdbm.helper.ActionContext;
>>>>> > +
>>>>> > +import java.util.concurrent.locks.Lock;
>>>>> > +import java.util.concurrent.locks.ReentrantLock;
>>>>> >
>>>>> >  import org.apache.directory.server.i18n.I18n;
>>>>> >
>>>>> > @@ -131,7 +141,13 @@ public class BTree<K, V> implements Exte
>>>>> >
>>>>> >     /** Serializer used for BPages of this tree */
>>>>> >     private transient BPage<K, V> bpageSerializer;
>>>>> > -
>>>>> > +
>>>>> > +    /** TRUE if underlying record manager is snapshot capable */
>>>>> > +    private transient boolean isActionCapable;
>>>>> > +
>>>>> > +
>>>>> > +    /** Big lock snychronizing all actions */
>>>>> > +    private transient Lock bigLock = new ReentrantLock();
>>>>> >
>>>>> >     /**
>>>>> >      * No-argument constructor used by serialization.
>>>>> > @@ -220,8 +236,27 @@ public class BTree<K, V> implements Exte
>>>>> >         this.bpageSerializer = new BPage<K, V>();
>>>>> >         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 );
>>>>> > +
>>>>> > +        try
>>>>> > +        {
>>>>> > +            this.recordId = recordManager.insert( this );
>>>>> > +        }
>>>>> > +        catch ( IOException e )
>>>>> > +        {
>>>>> > +            abortedAction = true;
>>>>> > +            this.abortAction( context );
>>>>> > +            throw e;
>>>>> > +        }
>>>>> > +        finally
>>>>> > +        {
>>>>> > +            if ( !abortedAction )
>>>>> > +                this.endAction( context );
>>>>> > +        }
>>>>> >     }
>>>>> >
>>>>> >
>>>>> > @@ -268,7 +303,7 @@ public class BTree<K, V> 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,58 +315,88 @@ public class BTree<K, V> implements Exte
>>>>> >             throw new IllegalArgumentException( I18n.err( I18n.ERR_524 ) );
>>>>> >         }
>>>>> >
>>>>> > -        BPage<K, V> rootPage = getRoot();
>>>>> > +
>>>>> > +        boolean abortedAction = false;
>>>>> > +        ActionContext context  = this.beginAction( false );
>>>>> > +
>>>>> >
>>>>> > -        if ( rootPage == null )
>>>>> > -        {
>>>>> > -            // BTree is currently empty, create a new root BPage
>>>>> > -            if ( DEBUG )
>>>>> > -            {
>>>>> > -                System.out.println( "BTree.insert() new root BPage" );
>>>>> > -            }
>>>>> > -
>>>>> > -            rootPage = new BPage<K, V>( this, key, value );
>>>>> > -            rootId = rootPage.getRecordId();
>>>>> > -            bTreeHeight = 1;
>>>>> > -            nbEntries.set( 1 );
>>>>> > -            recordManager.update( recordId, this );
>>>>> > -
>>>>> > -            return null;
>>>>> > -        }
>>>>> > -        else
>>>>> > +        if ( !isActionCapable )
>>>>> > +            bigLock.lock();
>>>>> > +
>>>>> > +        try
>>>>> >         {
>>>>> > -            BPage.InsertResult<K, V> insert = rootPage.insert( bTreeHeight, key, value, replace );
>>>>> > -            boolean dirty = false;
>>>>> > -
>>>>> > -            if ( insert.overflow != null )
>>>>> > +            BPage<K, V> rootPage = getRoot( false );
>>>>> > +
>>>>> > +            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<K, V>( this, rootPage, insert.overflow );
>>>>> > +                rootPage = new BPage<K, V>( this, key, value );
>>>>> >                 rootId = rootPage.getRecordId();
>>>>> > -                bTreeHeight += 1;
>>>>> > -                dirty = true;
>>>>> > -            }
>>>>> > -
>>>>> > -            if ( insert.existing == null )
>>>>> > -            {
>>>>> > -                nbEntries.getAndIncrement();
>>>>> > -                dirty = true;
>>>>> > +                bTreeHeight = 1;
>>>>> > +                nbEntries.set( 1 );
>>>>> > +                recordManager.update( recordId, this );
>>>>> > +
>>>>> > +                return null;
>>>>> >             }
>>>>> > -
>>>>> > -            if ( dirty )
>>>>> > +            else
>>>>> >             {
>>>>> > -                recordManager.update( recordId, this );
>>>>> > +                BPage.InsertResult<K, V> 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<K, V>( this, rootPage, insert.overflow );
>>>>> > +                    rootId = rootPage.getRecordId();
>>>>> > +                    bTreeHeight += 1;
>>>>> > +                    dirty = true;
>>>>> > +                }
>>>>> > +
>>>>> > +                if ( insert.existing == null )
>>>>> > +                {
>>>>> > +                    nbEntries.getAndIncrement();
>>>>> > +                    dirty = true;
>>>>> > +                }
>>>>> > +
>>>>> > +                if ( dirty )
>>>>> > +                {
>>>>> > +                    recordManager.update( recordId, this );
>>>>> > +                }
>>>>> > +
>>>>> > +                // insert might have returned an existing value
>>>>> > +                return insert.existing;
>>>>> >             }
>>>>> > +        }
>>>>> > +        catch ( IOException e )
>>>>> > +        {
>>>>> > +            abortedAction = true;
>>>>> > +            this.abortAction( context );
>>>>> > +            throw e;
>>>>> > +        }
>>>>> > +        finally
>>>>> > +        {
>>>>> > +            if ( !abortedAction )
>>>>> > +                this.endAction( context );
>>>>> >
>>>>> > -            // insert might have returned an existing value
>>>>> > -            return insert.existing;
>>>>> > +            if ( !isActionCapable )
>>>>> > +                bigLock.unlock();
>>>>> >         }
>>>>> > +
>>>>> >     }
>>>>> > +
>>>>> >
>>>>> >
>>>>> >     /**
>>>>> > @@ -341,52 +406,80 @@ public class BTree<K, V> 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<K, V> rootPage = getRoot();
>>>>> > +
>>>>> >
>>>>> > -        if ( rootPage == null )
>>>>> > -        {
>>>>> > -            return null;
>>>>> > -        }
>>>>> > -
>>>>> > -        boolean dirty = false;
>>>>> > -        BPage.RemoveResult<V> remove = rootPage.remove( bTreeHeight, key );
>>>>> > +        boolean abortedAction = false;
>>>>> > +        ActionContext context = this.beginAction( false );
>>>>> > +
>>>>> > +        if ( !isActionCapable )
>>>>> > +            bigLock.lock();
>>>>> >
>>>>> > -        if ( remove.underflow && rootPage.isEmpty() )
>>>>> > +        try
>>>>> >         {
>>>>> > -            bTreeHeight -= 1;
>>>>> > -            dirty = true;
>>>>> >
>>>>> > -            recordManager.delete( rootId );
>>>>> > +            BPage<K, V> rootPage = getRoot( false );
>>>>> >
>>>>> > -            if ( bTreeHeight == 0 )
>>>>> > +            if ( rootPage == null )
>>>>> >             {
>>>>> > -                rootId = 0;
>>>>> > +                return null;
>>>>> >             }
>>>>> > -            else
>>>>> > +
>>>>> > +            boolean dirty = false;
>>>>> > +            BPage.RemoveResult<K, V> remove = rootPage.remove( bTreeHeight, key );
>>>>> > +            if ( remove.pageNewCopy != null )
>>>>> > +                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();
>>>>> > +                }
>>>>> > +            }
>>>>> > +
>>>>> > +            if ( remove.value != null )
>>>>> >             {
>>>>> > -                rootId = rootPage.childBPage( pageSize - 1 ).getRecordId();
>>>>> > +                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 +489,52 @@ public class BTree<K, V> 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<K, V> browser = null;
>>>>> > +        Tuple<K, V> tuple = null;
>>>>> > +
>>>>> >         if ( key == null )
>>>>> >         {
>>>>> >             throw new IllegalArgumentException( I18n.err( I18n.ERR_523 ) );
>>>>> >         }
>>>>> >
>>>>> > -        BPage<K, V> rootPage = getRoot();
>>>>> > +        if ( !isActionCapable )
>>>>> > +            bigLock.lock();
>>>>> >
>>>>> > -        if ( rootPage == null )
>>>>> > -        {
>>>>> > -            return null;
>>>>> > -        }
>>>>> > -
>>>>> > -        Tuple<K, V> tuple = new Tuple<K, V>( null, null );
>>>>> > -        TupleBrowser<K, V> 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<K, V>( 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 tuple.getValue();
>>>>> > +                }
>>>>> >             }
>>>>> >             else
>>>>> >             {
>>>>> > -                return tuple.getValue();
>>>>> > +                return null;
>>>>> >             }
>>>>> >         }
>>>>> > -        else
>>>>> > +        finally
>>>>> >         {
>>>>> > -            return null;
>>>>> > +            if ( browser != null )
>>>>> > +                browser.close();
>>>>> > +
>>>>> > +            if ( !isActionCapable )
>>>>> > +                bigLock.unlock();
>>>>> >         }
>>>>> > +
>>>>> >     }
>>>>> >
>>>>> >
>>>>> > @@ -441,10 +546,10 @@ public class BTree<K, V> implements Exte
>>>>> >      * @return Value associated with the key, or a greater entry, or null if no
>>>>> >      *         greater entry was found.
>>>>> >      */
>>>>> > -    public synchronized Tuple<K, V> findGreaterOrEqual( K key ) throws IOException
>>>>> > +    public Tuple<K, V> findGreaterOrEqual( K key ) throws IOException
>>>>> >     {
>>>>> >         Tuple<K, V> tuple;
>>>>> > -        TupleBrowser<K, V> browser;
>>>>> > +        TupleBrowser<K, V> browser = null;
>>>>> >
>>>>> >         if ( key == null )
>>>>> >         {
>>>>> > @@ -453,16 +558,31 @@ public class BTree<K, V> implements Exte
>>>>> >             return null;
>>>>> >         }
>>>>> >
>>>>> > -        tuple = new Tuple<K, V>( null, null );
>>>>> > -        browser = browse( key );
>>>>> > +        if ( !isActionCapable )
>>>>> > +            bigLock.lock();
>>>>> >
>>>>> > -        if ( browser.getNext( tuple ) )
>>>>> > +        tuple = new Tuple<K, V>( null, null );
>>>>> > +        try
>>>>> >         {
>>>>> > -            return tuple;
>>>>> > +            browser = browse( key );
>>>>> > +
>>>>> > +            if ( browser.getNext( tuple ) )
>>>>> > +            {
>>>>> > +                return tuple;
>>>>> > +            }
>>>>> > +            else
>>>>> > +            {
>>>>> > +                return null;
>>>>> > +            }
>>>>> >         }
>>>>> > -        else
>>>>> > +        finally
>>>>> >         {
>>>>> > -            return null;
>>>>> > +            if ( browser != null )
>>>>> > +                browser.close();
>>>>> > +
>>>>> > +            if ( !isActionCapable )
>>>>> > +                bigLock.unlock();
>>>>> > +
>>>>> >         }
>>>>> >     }
>>>>> >
>>>>> > @@ -476,16 +596,26 @@ public class BTree<K, V> implements Exte
>>>>> >      *
>>>>> >      * @return Browser positionned at the beginning of the BTree.
>>>>> >      */
>>>>> > -    public synchronized TupleBrowser<K, V> browse() throws IOException
>>>>> > +    public TupleBrowser<K, V> browse() throws IOException
>>>>> >     {
>>>>> > -        BPage<K, V> rootPage = getRoot();
>>>>> > -
>>>>> > -        if ( rootPage == null )
>>>>> > +        TupleBrowser<K, V> browser = null;
>>>>> > +        ActionContext context = this.beginAction( true );
>>>>> > +        try
>>>>> >         {
>>>>> > -            return new EmptyBrowser(){};
>>>>> > +            BPage<K, V> rootPage = getRoot( true );
>>>>> > +
>>>>> > +            if ( rootPage == null )
>>>>> > +            {
>>>>> > +                this.endAction( context );
>>>>> > +                return new EmptyBrowser(){};
>>>>> > +            }
>>>>> > +            browser = rootPage.findFirst( context );
>>>>> > +        }
>>>>> > +        catch( IOException e )
>>>>> > +        {
>>>>> > +            this.abortAction( context );
>>>>> > +            throw e;
>>>>> >         }
>>>>> > -
>>>>> > -        TupleBrowser<K, V> browser = rootPage.findFirst();
>>>>> >
>>>>> >         return browser;
>>>>> >     }
>>>>> > @@ -503,19 +633,32 @@ public class BTree<K, V> implements Exte
>>>>> >      *            (Null is considered to be an "infinite" key)
>>>>> >      * @return Browser positioned just before the given key.
>>>>> >      */
>>>>> > -    public synchronized TupleBrowser<K, V> browse( K key ) throws IOException
>>>>> > +    public TupleBrowser<K, V> browse( K key ) throws IOException
>>>>> >     {
>>>>> > -        BPage<K, V> rootPage = getRoot();
>>>>> > +        TupleBrowser<K, V> browser = null;
>>>>> > +        ActionContext context = this.beginAction( true );
>>>>> >
>>>>> > -        if ( rootPage == null )
>>>>> > +        try
>>>>> >         {
>>>>> > -            return new EmptyBrowser(){};
>>>>> > +            BPage<K, V> rootPage = getRoot( true );
>>>>> > +
>>>>> > +            if ( rootPage == null )
>>>>> > +            {
>>>>> > +                this.endAction( context );
>>>>> > +                return new EmptyBrowser(){};
>>>>> > +            }
>>>>> > +
>>>>> > +            browser  = rootPage.find( rootPage.btree.bTreeHeight, key, context );
>>>>> > +        }
>>>>> > +        catch( IOException e )
>>>>> > +        {
>>>>> > +            this.abortAction( context );
>>>>> > +            throw e;
>>>>> >         }
>>>>> > -
>>>>> > -        TupleBrowser<K, V> browser = rootPage.find( bTreeHeight, key );
>>>>> >
>>>>> >         return browser;
>>>>> >     }
>>>>> > +
>>>>> >
>>>>> >
>>>>> >     /**
>>>>> > @@ -539,16 +682,27 @@ public class BTree<K, V> implements Exte
>>>>> >     /**
>>>>> >      * Return the root BPage<Object, Object>, or null if it doesn't exist.
>>>>> >      */
>>>>> > -    private BPage<K, V> getRoot() throws IOException
>>>>> > +    private BPage<K, V> getRoot( boolean readOnlyAction ) throws IOException
>>>>> >     {
>>>>> > -        if ( rootId == 0 )
>>>>> > +        BTree<K, V> bTreeCopy;
>>>>> > +
>>>>> > +        if ( readOnlyAction )
>>>>> > +        {
>>>>> > +            bTreeCopy = ( BTree<K, V> )recordManager.fetch( recordId );
>>>>> > +        }
>>>>> > +        else
>>>>> > +        {
>>>>> > +            bTreeCopy = this;
>>>>> > +        }
>>>>> > +
>>>>> > +        if ( bTreeCopy.rootId == 0 )
>>>>> >         {
>>>>> >             return null;
>>>>> >         }
>>>>> >
>>>>> > -        BPage<K, V> root = ( BPage<K, V> ) recordManager.fetch( rootId, bpageSerializer );
>>>>> > -        root.setRecordId( rootId );
>>>>> > -        root.btree = this;
>>>>> > +        BPage<K, V> root = ( BPage<K, V> ) recordManager.fetch( bTreeCopy.rootId, bpageSerializer );
>>>>> > +        root.setRecordId( bTreeCopy.rootId );
>>>>> > +        root.btree = bTreeCopy;
>>>>> >
>>>>> >         return root;
>>>>> >     }
>>>>> > @@ -616,6 +770,115 @@ public class BTree<K, V> 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 ).setCurrentActionContext( context );
>>>>> > +        }
>>>>> > +    }
>>>>> > +
>>>>> > +
>>>>> > +    ActionContext beginAction( boolean readOnly )
>>>>> > +    {
>>>>> > +        ActionContext context = null;
>>>>> > +        if ( isActionCapable )
>>>>> > +        {
>>>>> > +            context = ( ( ActionRecordManager )recordManager ).beginAction( readOnly );
>>>>> > +            this.setAsCurrentAction( context );
>>>>> > +        }
>>>>> > +        return context;
>>>>> > +    }
>>>>> > +
>>>>> > +
>>>>> > +    void endAction( ActionContext context )
>>>>> > +    {
>>>>> > +        if ( context != null )
>>>>> > +        {
>>>>> > +            assert( isActionCapable );
>>>>> > +            ( ( ActionRecordManager )recordManager ).endAction( context );
>>>>> > +            this.unsetAsCurrentAction( context );
>>>>> > +        }
>>>>> > +    }
>>>>> > +
>>>>> > +    void abortAction( ActionContext context )
>>>>> > +    {
>>>>> > +        if ( context != null )
>>>>> > +        {
>>>>> > +            assert( isActionCapable );
>>>>> > +            this.abortAction( context );
>>>>> > +            ( ( ActionRecordManager )recordManager ).abortAction( context );
>>>>> > +            this.unsetAsCurrentAction( context );
>>>>> > +        }
>>>>> > +
>>>>> > +    }
>>>>> > +
>>>>> > +
>>>>> > +    BPage<K,V> copyOnWrite( BPage<K,V> page) throws IOException
>>>>> > +    {
>>>>> > +        byte[] array;
>>>>> > +        array = this.bpageSerializer.serialize( page );
>>>>> > +        BPage<K,V> pageCopy = this.bpageSerializer.deserialize( array );
>>>>> > +        pageCopy.recordId = page.recordId;
>>>>> > +        pageCopy.btree = page.btree;
>>>>> > +        return pageCopy;
>>>>> > +    }
>>>>> > +
>>>>> > +    @SuppressWarnings("unchecked")
>>>>> > +    private BTree<K,V> copyOnWrite() throws IOException
>>>>> > +    {
>>>>> > +        ObjectOutputStream out = null;
>>>>> > +        ObjectInputStream in = null;
>>>>> > +        ByteArrayOutputStream bout = null;
>>>>> > +        ByteArrayInputStream bin = null;
>>>>> > +
>>>>> > +        BTree<K,V> tree;
>>>>> > +
>>>>> > +        try
>>>>> > +        {
>>>>> > +            bout = new ByteArrayOutputStream();
>>>>> > +            out = new ObjectOutputStream( bout );
>>>>> > +            out.writeObject( this );
>>>>> > +            out.flush();
>>>>> > +            byte[]  arr = bout.toByteArray();
>>>>> > +            bin = new ByteArrayInputStream( arr );
>>>>> > +            in =new ObjectInputStream( bin );
>>>>> > +            tree = ( BTree<K, 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 tree;
>>>>> > +    }
>>>>> > +
>>>>> > +
>>>>> > +
>>>>> >     public String toString()
>>>>> >     {
>>>>> >         StringBuilder sb = new StringBuilder();
>>>>> >
>>>>> > Modified: directory/apacheds/branches/apacheds-jdbm/jdbm/src/main/java/jdbm/helper/TupleBrowser.java
>>>>> > URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-jdbm/jdbm/src/main/java/jdbm/helper/TupleBrowser.java?rev=1161416&r1=1161415&r2=1161416&view=diff
>>>>> > ==============================================================================
>>>>> > --- directory/apacheds/branches/apacheds-jdbm/jdbm/src/main/java/jdbm/helper/TupleBrowser.java (original)
>>>>> > +++ directory/apacheds/branches/apacheds-jdbm/jdbm/src/main/java/jdbm/helper/TupleBrowser.java Thu Aug 25 06:58:36 2011
>>>>> > @@ -73,4 +73,11 @@ public abstract class TupleBrowser<K, V>
>>>>> >      *         no previous tuple.
>>>>> >      */
>>>>> >     public abstract boolean getPrevious( Tuple<K, V> 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/apacheds-jdbm/jdbm/src/main/java/jdbm/recman/BaseRecordManager.java
>>>>> > URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-jdbm/jdbm/src/main/java/jdbm/recman/BaseRecordManager.java?rev=1161416&r1=1161415&r2=1161416&view=diff
>>>>> > ==============================================================================
>>>>> > --- directory/apacheds/branches/apacheds-jdbm/jdbm/src/main/java/jdbm/recman/BaseRecordManager.java (original)
>>>>> > +++ directory/apacheds/branches/apacheds-jdbm/jdbm/src/main/java/jdbm/recman/BaseRecordManager.java Thu Aug 25 06:58:36 2011
>>>>> > @@ -52,6 +52,10 @@ import java.io.IOException;
>>>>> >
>>>>> >  import java.util.HashMap;
>>>>> >  import java.util.Map;
>>>>> > +import java.util.concurrent.ConcurrentHashMap;
>>>>> > +import java.util.concurrent.locks.Lock;
>>>>> > +import java.util.concurrent.locks.ReentrantLock;
>>>>> > +import java.util.concurrent.locks.Condition;
>>>>> >
>>>>> >  import org.apache.directory.server.i18n.I18n;
>>>>> >
>>>>> > @@ -109,6 +113,102 @@ public final class BaseRecordManager
>>>>> >      * the NAME_DIRECTORY_ROOT.
>>>>> >      */
>>>>> >     private Map<String,Long> 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<Long, LockElement> lockElements;
>>>>> >
>>>>> >
>>>>> >     /**
>>>>> > @@ -123,13 +223,14 @@ public final class BaseRecordManager
>>>>> >         pageMgr = new PageManager( recordFile );
>>>>> >         physMgr = new PhysicalRowIdManager( pageMgr );
>>>>> >         logMgr = new LogicalRowIdManager( pageMgr );
>>>>> > +        lockElements = new ConcurrentHashMap<Long, LockElement>();
>>>>> >     }
>>>>> >
>>>>> >
>>>>> >     /**
>>>>> >      * Get the underlying Transaction Manager
>>>>> >      */
>>>>> > -    public synchronized TransactionManager getTransactionManager() throws IOException
>>>>> > +    public TransactionManager getTransactionManager() throws IOException
>>>>> >     {
>>>>> >         checkIfClosed();
>>>>> >         return recordFile.getTxnMgr();
>>>>> > @@ -145,7 +246,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 +258,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 +291,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 +318,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 +333,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);
>>>>> > +        }
>>>>> >     }
>>>>> >
>>>>> >
>>>>> > @@ -250,7 +362,6 @@ public final class BaseRecordManager
>>>>> >         update( recid, obj, DefaultSerializer.INSTANCE );
>>>>> >     }
>>>>> >
>>>>> > -
>>>>> >     /**
>>>>> >      * Updates a record using a custom serializer.
>>>>> >      *
>>>>> > @@ -259,33 +370,45 @@ 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
>>>>> >     {
>>>>> > -        checkIfClosed();
>>>>> > +       LockElement element;
>>>>> > +
>>>>> > +       checkIfClosed();
>>>>> >
>>>>> >         if ( recid <= 0 )
>>>>> >         {
>>>>> >             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 +422,7 @@ public final class BaseRecordManager
>>>>> >         return fetch( recid, DefaultSerializer.INSTANCE );
>>>>> >     }
>>>>> >
>>>>> > -
>>>>> > +
>>>>> >     /**
>>>>> >      * Fetches a record using a custom serializer.
>>>>> >      *
>>>>> > @@ -308,26 +431,41 @@ 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;
>>>>> > -
>>>>> > -        checkIfClosed();
>>>>> > -
>>>>> > +       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 ) ) );
>>>>> > -
>>>>> > -        if ( DEBUG )
>>>>> > -        {
>>>>> > -            System.out.println( "BaseRecordManager.fetch() recid " + recid + " length " + data.length ) ;
>>>>> > -        }
>>>>> > -        return serializer.deserialize( data );
>>>>> > +
>>>>> > +       element = this.beginIO(recid, IOType.READ_IO);
>>>>> > +
>>>>> > +       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
>>>>> > +       {
>>>>> 
>>>>> > +               this.endIO(recid, element, IOType.READ_IO);
>>>>> > +       }
>>>>> > +
>>>>> > +       return result;
>>>>> > +
>>>>> >     }
>>>>> > +
>>>>> >
>>>>> >
>>>>> >     /**
>>>>> > @@ -347,7 +485,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 +498,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();
>>>>> >
>>>>> > @@ -411,7 +549,7 @@ public final class BaseRecordManager
>>>>> >     /**
>>>>> >      * Commit (make persistent) all changes since beginning of transaction.
>>>>> >      */
>>>>> > -    public synchronized void commit()
>>>>> > +    public void commit()
>>>>> >         throws IOException
>>>>> >     {
>>>>> >         checkIfClosed();
>>>>> > @@ -423,7 +561,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 +616,125 @@ 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() );
>>>>> > +    }
>>>>> > +
>>>>> >  }
>>>>> >
>>>>> >
>>>>> >
>>>>> 
>>>>> 
>>>>> 
>>>>> -- 
>>>>> Regards,
>>>>> Cordialement,
>>>>> Emmanuel Lécharny
>>>>> www.iktek.com
>>>> 
>>> 
>> 
> 
> 
> 
> 
> -- 
> Regards,
> Cordialement,
> Emmanuel Lécharny
> www.iktek.com
> 
> 
> 
> -- 
> Regards,
> Cordialement,
> Emmanuel Lécharny
> www.iktek.com


Mime
View raw message