/*- * See the file LICENSE for redistribution information. * * Copyright (c) 2005, 2010 Oracle and/or its affiliates. All rights reserved. * * $Id$ */ package db.txn; import com.sleepycat.bind.EntryBinding; import com.sleepycat.bind.serial.StoredClassCatalog; import com.sleepycat.bind.serial.SerialBinding; import com.sleepycat.bind.tuple.StringBinding; import com.sleepycat.db.Cursor; import com.sleepycat.db.CursorConfig; import com.sleepycat.db.Database; import com.sleepycat.db.DatabaseEntry; import com.sleepycat.db.DatabaseException; import com.sleepycat.db.DeadlockException; import com.sleepycat.db.Environment; import com.sleepycat.db.LockMode; import com.sleepycat.db.OperationStatus; import com.sleepycat.db.Transaction; import java.io.UnsupportedEncodingException; import java.util.Random; public class DBWriter extends Thread { private Database myDb = null; private Environment myEnv = null; private EntryBinding dataBinding = null; private Random generator = new Random(); private boolean passTxn = false; private static final int MAX_RETRY = 20; private static String[] keys = {"key 1", "key 2", "key 3", "key 4", "key 5", "key 6", "key 7", "key 8", "key 9", "key 10"}; // Constructor. Get our DB handles from here // This consturctor allows us to indicate whether the // txn handle should be handed to countRecords() DBWriter(Environment env, Database db, StoredClassCatalog scc, boolean passtxn) throws DatabaseException { myDb = db; myEnv = env; dataBinding = new SerialBinding(scc, PayloadData.class); passTxn = passtxn; } // Constructor. Get our DB handles from here DBWriter(Environment env, Database db, StoredClassCatalog scc) throws DatabaseException { myDb = db; myEnv = env; dataBinding = new SerialBinding(scc, PayloadData.class); } // Thread method that writes a series of records // to the database using transaction protection. // Deadlock handling is demonstrated here. public void run () { Transaction txn = null; // Perform 50 transactions for (int i=0; i<50; i++) { boolean retry = true; int retry_count = 0; // while loop is used for deadlock retries while (retry) { // try block used for deadlock detection and // general db exception handling try { // Get a transaction txn = myEnv.beginTransaction(null, null); // Write 10 records to the db // for each transaction for (int j = 0; j < 10; j++) { // Get the key DatabaseEntry key = new DatabaseEntry(); StringBinding.stringToEntry(keys[j], key); // Get the data PayloadData pd = new PayloadData(i+j, getName(), generator.nextDouble()); DatabaseEntry data = new DatabaseEntry(); dataBinding.objectToEntry(pd, data); // Do the put myDb.put(txn, key, data); } // commit System.out.println(getName() + " : committing txn : " + i); // This code block allows us to decide if txn handle is // passed to countRecords() // // TxnGuideInMemory requires a txn handle be handed to // countRecords(). The code self deadlocks if you don't. // TxnGuide has no such requirement because it supports // uncommitted reads. Transaction txnHandle = null; if (passTxn) { txnHandle = txn; } System.out.println(getName() + " : Found " + countRecords(txnHandle) + " records in the database."); try { txn.commit(); txn = null; } catch (DatabaseException e) { System.err.println("Error on txn commit: " + e.toString()); } retry = false; } catch (DeadlockException de) { System.out.println("################# " + getName() + " : caught deadlock"); // retry if necessary if (retry_count < MAX_RETRY) { System.err.println(getName() + " : Retrying operation."); retry = true; retry_count++; } else { System.err.println(getName() + " : out of retries. Giving up."); retry = false; } } catch (DatabaseException e) { // abort and don't retry retry = false; System.err.println(getName() + " : caught exception: " + e.toString()); System.err.println(getName() + " : errno: " + e.getErrno()); e.printStackTrace(); } finally { if (txn != null) { try { txn.abort(); } catch (Exception e) { System.err.println("Error aborting transaction: " + e.toString()); e.printStackTrace(); } } } } } } // This simply counts the number of records contained in the // database and returns the result. You can use this method // in three ways: // // First call it with an active txn handle. // // Secondly, configure the cursor for dirty reads // // Third, call countRecords AFTER the writer has committed // its transaction. // // If you do none of these things, the writer thread will // self-deadlock. // // Note that this method exists only for illustrative purposes. // A more straight-forward way to count the number of records in // a database is to use the Database.getStats() method. private int countRecords(Transaction txn) throws DatabaseException { DatabaseEntry key = new DatabaseEntry(); DatabaseEntry data = new DatabaseEntry(); int count = 0; Cursor cursor = null; try { // Get the cursor CursorConfig cc = new CursorConfig(); // setReadUncommitted is ignored if the database was not // opened for uncommitted read support. TxnGuide opens // its database in this way, TxnGuideInMemory does not. cc.setReadUncommitted(true); cursor = myDb.openCursor(txn, cc); while (cursor.getNext(key, data, LockMode.DEFAULT) == OperationStatus.SUCCESS) { count++; } } finally { if (cursor != null) { cursor.close(); } } return count; } }