Configuring Read Committed Isolation

You can configure JE to use degree 2 isolation (see Transactions and Concurrency) by configuring it to perform read committed isolation. Read committed isolation will cause data to be isolated so long as it is being addressed by the cursor. However, once the cursor is done reading the record, the cursor immediately releases its lock on that record. This means that the data the cursor has read and released may change before the cursor has closed and before the transaction has ended.

When you configure your application for this level of isolation, you may see better performance throughput because there are fewer read locks being held by your transactions. Read committed isolation is most useful when you have a cursor that is reading and/or writing records in a single direction, and that does not ever have to go back to re-read those same records. In this case, you can allow JE to release read locks as it goes, rather than hold them for the life of the cursor.

To configure your application to use committed reads, create your transaction such that it allows committed reads. You do this by specifying true to TransactionConfig.setReadComitted(). For example:

package je.gettingStarted;

import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.Environment;
import com.sleepycat.je.LockMode;
import com.sleepycat.je.Transaction;
import com.sleepycat.je.TransactionConfig;

...

Database myDb = null;
Environment myEnv = null;
Transaction txn = null;

try {

    // Environment and database open omitted

    ...

    TransactionConfig tc = new TransactionConfig();
    tc.setReadCommitted(true); // Use committed read isolation
    txn = myEnv.beginTransaction(null, tc);

    DatabaseEntry theKey = 
        new DatabaseEntry((new String("theKey")).getBytes("UTF-8"));
    DatabaseEntry theData = new DatabaseEntry();

    myDb.get(txn, theKey, theData, LockMode.DEFAULT); 
} catch (Exception e) {
    // Exception handling goes here
}

You can also configure read committed isolation on a read-by-read basis by specifying LockMode.READ_COMMITTED:

package je.gettingStarted;

import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.Environment;
import com.sleepycat.je.LockMode;
import com.sleepycat.je.Transaction;

...

Database myDb = null;
Environment myEnv = null;
Transaction txn = null;

try {

    // Environment and database open omitted

    ...

    txn = myEnv.beginTransaction(null, null);

    DatabaseEntry theKey = 
        new DatabaseEntry((new String("theKey")).getBytes("UTF-8"));
    DatabaseEntry theData = new DatabaseEntry();

    myDb.get(txn, theKey, theData, LockMode.READ_COMMITTED);
} catch (Exception e) {
    // Exception handling goes here
} 

When using cursors, you can specify the committed read behavior as described above, or you can specify it using CursorConfig.setReadCommitted():

package je.gettingStarted;

import com.sleepycat.je.Cursor;
import com.sleepycat.je.CursorConfig;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.Environment;
import com.sleepycat.je.Transaction;
import com.sleepycat.je.LockMode;

...

Cursor cursor = null;
Database myDb = null;
Environment myEnv = null;
Transaction txn = null;

try {

    // Environment and database open omitted

    ...

    DatabaseEntry theKey = 
        new DatabaseEntry((new String("theKey")).getBytes("UTF-8"));
    DatabaseEntry theData = new DatabaseEntry();

    // Start a transaction
    txn = myEnv.beginTransaction(null, null);

    // Open a cursor using the transaction
    CursorConfig cc = new CursorConfig();
    cc.setReadCommitted(true);             // Perform committed reads
    cursor = myDb.openCursor(txn, cc);
 
    cursor.getSearchKey(theKey, theData, LockMode.DEFAULT); 
} catch (Exception e) {
    // Exception handling goes here
}