This discussion is archived
7 Replies Latest reply: Sep 25, 2007 12:35 PM by greybird RSS

Transaction abort after "replacing" a record in a sorted duplicate DB

591718 Newbie
Currently Being Moderated
I have a sorted duplicate transactional Database, and if I abort a transaction after "replacing" a record -- by doing cursor.delete() followed by cursor.put(...), the record remains deleted. It should have been restored during the abort rollback.

I noticed that if I just do the cursor.delete(), and don't follow with the cursor.put(...), the abort seems to rollback correctly.

Is there something about the way I'm replacing the record (delete -> put) that is not right, or could there be a bug in the DB JE code?

Here are the relevant parts of the code with some error checking removed:

txn = mDBEnv.beginTransaction(null, null);
cursor = mUserBookmarksDatabase.openCursor(txn, null);

DatabaseEntry accountKeyEntry = new DatabaseEntry();
EntryBinding accountKeyBinding = TupleBinding.getPrimitiveBinding(Integer.class);
accountKeyBinding.objectToEntry(iAccountID, accountKeyEntry);

DatabaseEntry userBookmarkDataKeyEntry = new DatabaseEntry();
EntryBinding userBookmarkDataKeyBinding = TupleBinding.getPrimitiveBinding(Integer.class);
userBookmarkDataKeyBinding.objectToEntry(new Integer(iBookmarkTS), userBookmarkDataKeyEntry);
UserBookmarkData userBookmarkData = null;

UserBookmarkTupleBinding userBookmarkDataBinding = DataHelper.instance().createUserBookmarkTupleBinding();

if ((status = cursor.getSearchBothRange(accountKeyEntry, userBookmarkDataKeyEntry, LockMode.RMW)) == OperationStatus.SUCCESS) {
cursor.delete();

DatabaseEntry accountKeyEntry = new DatabaseEntry();
EntryBinding accountKeyBinding = TupleBinding.getPrimitiveBinding(Integer.class);
accountKeyBinding.objectToEntry(iAccountID, accountKeyEntry);

DatabaseEntry userBookmarkDataEntry = new DatabaseEntry();
UserBookmarkTupleBinding userBookmarkDataBinding = DataHelper.instance().createUserBookmarkTupleBinding();
userBookmarkDataBinding.objectToEntry(iUserBookmarkData, userBookmarkDataEntry);

// This "put" seems to break things if I abort the transaction
cursor.put(accountKeyEntry, userBookmarkDataEntry);
}
  • 1. Re: Transaction abort after "replacing" a record in a sorted duplicate DB
    greybird Expert
    Currently Being Moderated
    Hi,

    We don't have any known problems with transaction abort and the test program below doesn't reproduce the problem. I don't know of any reason that a Cursor put after delete would be a special case.

    Could you please send a small reproducible test?

    Are you closing the cursor before you abort the transaction?

    --mark
    import java.io.File;
    import com.sleepycat.bind.tuple.IntegerBinding;
    import com.sleepycat.je.Cursor;
    import com.sleepycat.je.Database;
    import com.sleepycat.je.DatabaseConfig;
    import com.sleepycat.je.DatabaseEntry;
    import com.sleepycat.je.DatabaseException; 
    import com.sleepycat.je.Environment;
    import com.sleepycat.je.EnvironmentConfig;
    import com.sleepycat.je.OperationStatus;
    import com.sleepycat.je.Transaction;
            
    public class Test {
        public static void main(String[] args) throws DatabaseException {
            EnvironmentConfig envConfig = new EnvironmentConfig();
            envConfig.setAllowCreate(true);
            envConfig.setTransactional(true);
            Environment env = new Environment(new File("./tmp"), envConfig);
            
            DatabaseConfig dbConfig = new DatabaseConfig();
            dbConfig.setAllowCreate(true);
            dbConfig.setTransactional(true);
            dbConfig.setSortedDuplicates(true);
            Database db = env.openDatabase(null, "test", dbConfig);
    
            DatabaseEntry key = new DatabaseEntry();
            DatabaseEntry data = new DatabaseEntry();
            
            IntegerBinding.intToEntry(1, key);
            IntegerBinding.intToEntry(1, data);
            OperationStatus status = db.put(null, key, data);
            assert status == OperationStatus.SUCCESS;
    
            IntegerBinding.intToEntry(1, key);
            IntegerBinding.intToEntry(2, data);
            status = db.put(null, key, data);
            assert status == OperationStatus.SUCCESS;
    
            Transaction txn = env.beginTransaction(null, null);
            Cursor cursor = db.openCursor(txn, null);
    
            IntegerBinding.intToEntry(1, key);
            status = cursor.getSearchKey(key, data, null);
            assert status == OperationStatus.SUCCESS;
            assert IntegerBinding.entryToInt(key) == 1;
            assert IntegerBinding.entryToInt(data) == 1;
    
            status = cursor.delete();
            assert status == OperationStatus.SUCCESS;
    
            IntegerBinding.intToEntry(1, key);
            IntegerBinding.intToEntry(3, data);
            status = cursor.put(key, data);
            assert status == OperationStatus.SUCCESS;
    
            cursor.close();
            txn.abort();
    
            IntegerBinding.intToEntry(1, key);
            status = db.get(null, key, data, null);
            assert status == OperationStatus.SUCCESS;
            assert IntegerBinding.entryToInt(key) == 1;
            assert IntegerBinding.entryToInt(data) == 1;
    
            db.close();
            env.close();
        }
    }
  • 2. Re: Transaction abort after "replacing" a record in a sorted duplicate DB
    591718 Newbie
    Currently Being Moderated
    I now have a small test case that shows the problem. What seems to be happening is that the data portion of the record is not being rolled-back, but I may be doing something screwy in the way I'm using a custom comparator and cursor.getSearchBothRange(...).

    Is there a place to which I can upload or email my four java source files?

    Thanks.
  • 3. Re: Transaction abort after "replacing" a record in a sorted duplicate DB
    greybird Expert
    Currently Being Moderated
    Ah, you're using a custom comparator, that's something we should look at first.

    You can send a zip file to my email address: mark.hayes @ the obvious .com.

    --mark                                                                                                                                                                                                                                                                                                                                               
  • 4. Re: Transaction abort after "replacing" a record in a sorted duplicate DB
    Linda Lee Journeyer
    Currently Being Moderated
    In addition to sending your test case to us, could you also first take the step of running your example with the default instead of the custom comparator? That would help you further narrow down where the problem might lie.

    Thanks,

    Linda
  • 5. Re: Transaction abort after "replacing" a record in a sorted duplicate DB
    591718 Newbie
    Currently Being Moderated
    You are correct in your hunch that the custom comparator is related the the problem -- if I use the default comparator, things work fine.

    I'll send my sample code along in a few minutes.
  • 6. Re: Transaction abort after "replacing" a record in a sorted duplicate DB
    greybird Expert
    Currently Being Moderated
    We are currently working on this problem with this user off-forum. We'll report back here with the resolution.
    --mark                                                                                                                                                                                                                                               
  • 7. Re: Transaction abort after "replacing" a record in a sorted duplicate DB
    greybird Expert
    Currently Being Moderated
    This problem has been fixed and the fix will be in the next 3.2.x release of JE. If you need this fix prior to the next release, please send me email -- mark.hayes. Here is the change log entry for the bug:
    Fix a bug where the wrong key or duplicate data could be returned by queries
    when a custom btree or duplicate comparator is configured that does not
    compare all bytes.  The bug could occur in the following situation:

    + In transaction T, a previously existing record with key K is deleted.
    + In T, a record with key K' is inserted, where K' and K are not identical byte
      arrays, but are considered equal by the custom key comparator.
    + T is aborted.

    If the timing is right, the previously existing record will not be restored
    correctly when the transaction is aborted.  Instead of having key K it will
    have key K'.  When the previously existing record is returned by a query, key
    K' will be incorrectly returned.

    The same thing could occur for data elements (rather than keys) when using a
    duplicates database with a custom duplicate comparator (rather than a custom
    btree comparator).

    [#15704]
    --mark