1/*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 2005-2009 Oracle. All rights reserved. 5 * 6 * $Id$ 7 */ 8 9package db.txn; 10 11import com.sleepycat.bind.EntryBinding; 12import com.sleepycat.bind.serial.StoredClassCatalog; 13import com.sleepycat.bind.serial.SerialBinding; 14import com.sleepycat.bind.tuple.StringBinding; 15 16import com.sleepycat.db.Cursor; 17import com.sleepycat.db.CursorConfig; 18import com.sleepycat.db.Database; 19import com.sleepycat.db.DatabaseEntry; 20import com.sleepycat.db.DatabaseException; 21import com.sleepycat.db.DeadlockException; 22import com.sleepycat.db.Environment; 23import com.sleepycat.db.LockMode; 24import com.sleepycat.db.OperationStatus; 25import com.sleepycat.db.Transaction; 26 27import java.io.UnsupportedEncodingException; 28import java.util.Random; 29 30public class DBWriter extends Thread 31{ 32 private Database myDb = null; 33 private Environment myEnv = null; 34 private EntryBinding dataBinding = null; 35 private Random generator = new Random(); 36 private boolean passTxn = false; 37 38 39 private static final int MAX_RETRY = 20; 40 41 private static String[] keys = {"key 1", "key 2", "key 3", 42 "key 4", "key 5", "key 6", 43 "key 7", "key 8", "key 9", 44 "key 10"}; 45 46 47 // Constructor. Get our DB handles from here 48 // This consturctor allows us to indicate whether the 49 // txn handle should be handed to countRecords() 50 DBWriter(Environment env, Database db, StoredClassCatalog scc, 51 boolean passtxn) 52 53 throws DatabaseException { 54 myDb = db; 55 myEnv = env; 56 dataBinding = new SerialBinding(scc, PayloadData.class); 57 58 passTxn = passtxn; 59 } 60 61 // Constructor. Get our DB handles from here 62 DBWriter(Environment env, Database db, StoredClassCatalog scc) 63 64 throws DatabaseException { 65 myDb = db; 66 myEnv = env; 67 dataBinding = new SerialBinding(scc, PayloadData.class); 68 } 69 70 // Thread method that writes a series of records 71 // to the database using transaction protection. 72 // Deadlock handling is demonstrated here. 73 public void run () { 74 Transaction txn = null; 75 76 // Perform 50 transactions 77 for (int i=0; i<50; i++) { 78 79 boolean retry = true; 80 int retry_count = 0; 81 // while loop is used for deadlock retries 82 while (retry) { 83 // try block used for deadlock detection and 84 // general db exception handling 85 try { 86 87 // Get a transaction 88 txn = myEnv.beginTransaction(null, null); 89 90 // Write 10 records to the db 91 // for each transaction 92 for (int j = 0; j < 10; j++) { 93 // Get the key 94 DatabaseEntry key = new DatabaseEntry(); 95 StringBinding.stringToEntry(keys[j], key); 96 97 // Get the data 98 PayloadData pd = new PayloadData(i+j, getName(), 99 generator.nextDouble()); 100 DatabaseEntry data = new DatabaseEntry(); 101 dataBinding.objectToEntry(pd, data); 102 103 // Do the put 104 myDb.put(txn, key, data); 105 } 106 107 // commit 108 System.out.println(getName() + " : committing txn : " + i); 109 110 // This code block allows us to decide if txn handle is 111 // passed to countRecords() 112 // 113 // TxnGuideInMemory requires a txn handle be handed to 114 // countRecords(). The code self deadlocks if you don't. 115 // TxnGuide has no such requirement because it supports 116 // uncommitted reads. 117 Transaction txnHandle = null; 118 if (passTxn) { txnHandle = txn; } 119 120 System.out.println(getName() + " : Found " + 121 countRecords(txnHandle) + " records in the database."); 122 try { 123 txn.commit(); 124 txn = null; 125 } catch (DatabaseException e) { 126 System.err.println("Error on txn commit: " + 127 e.toString()); 128 } 129 retry = false; 130 131 } catch (DeadlockException de) { 132 System.out.println("################# " + getName() + 133 " : caught deadlock"); 134 // retry if necessary 135 if (retry_count < MAX_RETRY) { 136 System.err.println(getName() + 137 " : Retrying operation."); 138 retry = true; 139 retry_count++; 140 } else { 141 System.err.println(getName() + 142 " : out of retries. Giving up."); 143 retry = false; 144 } 145 } catch (DatabaseException e) { 146 // abort and don't retry 147 retry = false; 148 System.err.println(getName() + 149 " : caught exception: " + e.toString()); 150 System.err.println(getName() + 151 " : errno: " + e.getErrno()); 152 e.printStackTrace(); 153 } finally { 154 if (txn != null) { 155 try { 156 txn.abort(); 157 } catch (Exception e) { 158 System.err.println("Error aborting transaction: " + 159 e.toString()); 160 e.printStackTrace(); 161 } 162 } 163 } 164 } 165 } 166 } 167 168 // This simply counts the number of records contained in the 169 // database and returns the result. You can use this method 170 // in three ways: 171 // 172 // First call it with an active txn handle. 173 // 174 // Secondly, configure the cursor for dirty reads 175 // 176 // Third, call countRecords AFTER the writer has committed 177 // its transaction. 178 // 179 // If you do none of these things, the writer thread will 180 // self-deadlock. 181 // 182 // Note that this method exists only for illustrative purposes. 183 // A more straight-forward way to count the number of records in 184 // a database is to use the Database.getStats() method. 185 private int countRecords(Transaction txn) throws DatabaseException { 186 DatabaseEntry key = new DatabaseEntry(); 187 DatabaseEntry data = new DatabaseEntry(); 188 int count = 0; 189 Cursor cursor = null; 190 191 try { 192 // Get the cursor 193 CursorConfig cc = new CursorConfig(); 194 // setReadUncommitted is ignored if the database was not 195 // opened for uncommitted read support. TxnGuide opens 196 // its database in this way, TxnGuideInMemory does not. 197 cc.setReadUncommitted(true); 198 cursor = myDb.openCursor(txn, cc); 199 while (cursor.getNext(key, data, LockMode.DEFAULT) == 200 OperationStatus.SUCCESS) { 201 202 count++; 203 } 204 } finally { 205 if (cursor != null) { 206 cursor.close(); 207 } 208 } 209 210 return count; 211 212 } 213} 214