DatabaseConfig dbConfig = new DatabaseConfig();
dbConfig.setTransactional(true);
dbConfig.setAllowCreate(true);
dbConfig.setSortedDuplicates(true);
final Database db = env.openDatabase(dbTxn, "foo", dbConfig);
dbTxn.commit();
final Transaction txn = env.beginTransaction(null, null);
sequence = 0;
/**
* The sequence between the two tester threads is:
*
* tester2: write 1/1 into the database. This causes the initial tree
* to be created (IN/BIN/LN). Flush that out to the disk with a full
* checkpoint. Signal tester1 and wait.
*
* tester1: Lock the MapLN for "foo" db. Signal tester2 and wait.
*
* tester2: Add 2/2 to the tree which causes the BIN to be dirtied.
* Signal tester1 to continue, perform a full checkpoint which will
* causes the root IN to be dirtied and flushed. DbTree.modifyDbRoot
* will block on the MapLN lock held by tester1.
*
* tester1: while tester2 is blocking on the MapLN lock, this thread is
* sleeping. When it wakes up, it releases the MapLN lock by aborting
* the transaction.
*
* tester2: modifyDbRoot finally acquires the write lock on foo-db's
* MapLN write lock, performs the update to the DbTree and returns from
* the sync().
*/
JUnitThread tester1 =
new JUnitThread("testSR11293DbTreeLocker") {
public void testBody() {
try {
/* Wait for tester2. */
while (sequence < 1) {
Thread.yield();
}
/* Lock the MapLN for the database. */
DatabaseId fooId =
DbInternal.dbGetDatabaseImpl(db).getId();
DatabaseImpl fooDb = dbTree.getDb(fooId, 500000L);
assert fooDb != null;
sequence++;
/* Wait for tester2. */
while (sequence < 3) {
Thread.yield();
}
try {
Thread.sleep(3000);
} catch (Exception E) {
}
try {
txn.abort();
db.close();
env.close();
} catch (DatabaseException DBE) {
DBE.printStackTrace();
fail("unexpected exception: " + DBE);
}
} catch (DatabaseException DBE) {
DBE.printStackTrace();
fail("caught DatabaseException " + DBE);
}
}
};
JUnitThread tester2 =
new JUnitThread("testSR11293DbWriter") {
public void testBody() {
try {
DatabaseEntry key =
new DatabaseEntry(new byte[] { 1 });
DatabaseEntry data =
new DatabaseEntry(new byte[] { 1 });
assertEquals(OperationStatus.SUCCESS,
db.put(null, key, data));
env.sync();
sequence++;
while (sequence < 2) {
Thread.yield();
}
key.setData(new byte[] { 2 });
data.setData(new byte[] { 2 });
assertEquals(OperationStatus.SUCCESS,
db.put(null, key, data));
sequence++;
env.sync();
} catch (DatabaseException DBE) {
DBE.printStackTrace();
fail("unexpected exception: " + DBE);
}
}
};
tester1.start();
tester2.start();
tester1.finishTest();
tester2.finishTest();
EnvironmentConfig recoveryConfig = TestUtils.initEnvConfig();
recoveryConfig.setConfigParam
(EnvironmentParams.ENV_RUN_CHECKPOINTER.getName(), "false");
recoveryConfig.setConfigParam
(EnvironmentParams.ENV_RUN_CLEANER.getName(), "false");
recoveryConfig.setConfigParam
(EnvironmentParams.ENV_RUN_EVICTOR.getName(), "false");
env = new Environment(envHome, recoveryConfig);
dbConfig.setAllowCreate(false);
dbConfig.setTransactional(false);
Database db2 = env.openDatabase(null, "foo", dbConfig);
Cursor c = db2.openCursor(null, null);
DatabaseEntry key = new DatabaseEntry();
DatabaseEntry data = new DatabaseEntry();
assertEquals(OperationStatus.SUCCESS,
c.getNext(key, data, LockMode.DEFAULT));
assertEquals((key.getData())[0], 1);
assertEquals((data.getData())[0], 1);
assertEquals(OperationStatus.SUCCESS,
c.getNext(key, data, LockMode.DEFAULT));
assertEquals((key.getData())[0], 2);
assertEquals((data.getData())[0], 2);
assertEquals(OperationStatus.NOTFOUND,
c.getNext(key, data, LockMode.DEFAULT));
c.close();
db2.close();
env.close();
}