001: /*-
002: * See the file LICENSE for redistribution information.
003: *
004: * Copyright (c) 2002,2008 Oracle. All rights reserved.
005: *
006: * $Id: TxnManager.java,v 1.59.2.4 2008/01/07 15:14:17 cwl Exp $
007: */
008:
009: package com.sleepycat.je.txn;
010:
011: import java.util.Collections;
012: import java.util.HashMap;
013: import java.util.HashSet;
014: import java.util.Iterator;
015: import java.util.Map;
016: import java.util.Set;
017:
018: import javax.transaction.xa.Xid;
019:
020: import com.sleepycat.je.DatabaseException;
021: import com.sleepycat.je.LockStats;
022: import com.sleepycat.je.StatsConfig;
023: import com.sleepycat.je.Transaction;
024: import com.sleepycat.je.TransactionConfig;
025: import com.sleepycat.je.TransactionStats;
026: import com.sleepycat.je.dbi.EnvironmentImpl;
027: import com.sleepycat.je.dbi.MemoryBudget;
028: import com.sleepycat.je.latch.Latch;
029: import com.sleepycat.je.latch.LatchSupport;
030: import com.sleepycat.je.utilint.DbLsn;
031:
032: /**
033: * Class to manage transactions. Basically a Set of all transactions with add
034: * and remove methods and a latch around the set.
035: */
036: public class TxnManager {
037:
038: /*
039: * All NullTxns share the same id so as not to eat from the id number
040: * space.
041: */
042: static final long NULL_TXN_ID = -1;
043: private static final String DEBUG_NAME = TxnManager.class.getName();
044:
045: private LockManager lockManager;
046: private EnvironmentImpl env;
047: private Latch allTxnLatch;
048: private Set allTxns;
049: /* Maps Xids to Txns. */
050: private Map allXATxns;
051: /* Maps Threads to Txns when there are thread implied transactions. */
052: private Map thread2Txn;
053: private long lastUsedTxnId;
054: private int nActiveSerializable;
055:
056: /* Locker Stats */
057: private int numCommits;
058: private int numAborts;
059: private int numXAPrepares;
060: private int numXACommits;
061: private int numXAAborts;
062:
063: public TxnManager(EnvironmentImpl env) throws DatabaseException {
064:
065: if (EnvironmentImpl.getFairLatches()) {
066: lockManager = new LatchedLockManager(env);
067: } else {
068: if (env.isNoLocking()) {
069: lockManager = new DummyLockManager(env);
070: } else {
071: lockManager = new SyncedLockManager(env);
072: }
073: }
074:
075: this .env = env;
076: allTxns = new HashSet();
077: allTxnLatch = LatchSupport.makeLatch(DEBUG_NAME, env);
078: allXATxns = Collections.synchronizedMap(new HashMap());
079: thread2Txn = Collections.synchronizedMap(new HashMap());
080:
081: numCommits = 0;
082: numAborts = 0;
083: numXAPrepares = 0;
084: numXACommits = 0;
085: numXAAborts = 0;
086: lastUsedTxnId = 0;
087: }
088:
089: /**
090: * Set the txn id sequence.
091: */
092: synchronized public void setLastTxnId(long lastId) {
093: this .lastUsedTxnId = lastId;
094: }
095:
096: /**
097: * Get the last used id, for checkpoint info.
098: */
099: public synchronized long getLastTxnId() {
100: return lastUsedTxnId;
101: }
102:
103: /**
104: * Get the next transaction id to use.
105: */
106: synchronized long incTxnId() {
107: return ++lastUsedTxnId;
108: }
109:
110: /**
111: * Create a new transaction.
112: * @param parent for nested transactions, not yet supported
113: * @param txnConfig specifies txn attributes
114: * @return the new txn
115: */
116: public Txn txnBegin(Transaction parent, TransactionConfig txnConfig)
117: throws DatabaseException {
118:
119: if (parent != null) {
120: throw new DatabaseException(
121: "Nested transactions are not supported yet.");
122: }
123:
124: return new Txn(env, txnConfig);
125: }
126:
127: /**
128: * Give transactions and environment access to lock manager.
129: */
130: public LockManager getLockManager() {
131: return lockManager;
132: }
133:
134: /**
135: * Called when txn is created.
136: */
137: void registerTxn(Txn txn) throws DatabaseException {
138:
139: allTxnLatch.acquire();
140: allTxns.add(txn);
141: if (txn.isSerializableIsolation()) {
142: nActiveSerializable++;
143: }
144: allTxnLatch.release();
145: }
146:
147: /**
148: * Called when txn ends.
149: */
150: void unRegisterTxn(Txn txn, boolean isCommit)
151: throws DatabaseException {
152:
153: allTxnLatch.acquire();
154: try {
155: allTxns.remove(txn);
156: /* Remove any accumulated MemoryBudget delta for the Txn. */
157: env.getMemoryBudget().updateMiscMemoryUsage(
158: txn.getAccumulatedDelta() - txn.getInMemorySize());
159: if (isCommit) {
160: numCommits++;
161: } else {
162: numAborts++;
163: }
164: if (txn.isSerializableIsolation()) {
165: nActiveSerializable--;
166: }
167: } finally {
168: allTxnLatch.release();
169: }
170: }
171:
172: /**
173: * Called when txn is created.
174: */
175: public void registerXATxn(Xid xid, Txn txn, boolean isPrepare)
176: throws DatabaseException {
177:
178: if (!allXATxns.containsKey(xid)) {
179: allXATxns.put(xid, txn);
180: env.getMemoryBudget().updateMiscMemoryUsage(
181: MemoryBudget.HASHMAP_ENTRY_OVERHEAD);
182: }
183:
184: if (isPrepare) {
185: numXAPrepares++;
186: }
187: }
188:
189: /**
190: * Called when txn ends.
191: */
192: void unRegisterXATxn(Xid xid, boolean isCommit)
193: throws DatabaseException {
194:
195: if (allXATxns.remove(xid) == null) {
196: throw new DatabaseException("XA Transaction " + xid
197: + " can not be unregistered.");
198: }
199: env.getMemoryBudget().updateMiscMemoryUsage(
200: 0 - MemoryBudget.HASHMAP_ENTRY_OVERHEAD);
201: if (isCommit) {
202: numXACommits++;
203: } else {
204: numXAAborts++;
205: }
206: }
207:
208: /**
209: * Retrieve a Txn object from an Xid.
210: */
211: public Txn getTxnFromXid(Xid xid) throws DatabaseException {
212:
213: return (Txn) allXATxns.get(xid);
214: }
215:
216: /**
217: * Called when txn is assoc'd with this thread.
218: */
219: public void setTxnForThread(Transaction txn) {
220:
221: Thread curThread = Thread.currentThread();
222: thread2Txn.put(curThread, txn);
223: }
224:
225: /**
226: * Called when txn is assoc'd with this thread.
227: */
228: public Transaction unsetTxnForThread() throws DatabaseException {
229:
230: Thread curThread = Thread.currentThread();
231: return (Transaction) thread2Txn.remove(curThread);
232: }
233:
234: /**
235: * Retrieve a Txn object for this Thread.
236: */
237: public Transaction getTxnForThread() throws DatabaseException {
238:
239: return (Transaction) thread2Txn.get(Thread.currentThread());
240: }
241:
242: public Xid[] XARecover() throws DatabaseException {
243:
244: Set xidSet = allXATxns.keySet();
245: Xid[] ret = new Xid[xidSet.size()];
246: ret = (Xid[]) xidSet.toArray(ret);
247:
248: return ret;
249: }
250:
251: /**
252: * Returns whether there are any active serializable transactions,
253: * excluding the transaction given (if non-null). This is intentionally
254: * returned without latching, since latching would not make the act of
255: * reading an integer more atomic than it already is.
256: */
257: public boolean areOtherSerializableTransactionsActive(
258: Locker excludeLocker) {
259: int exclude = (excludeLocker != null && excludeLocker
260: .isSerializableIsolation()) ? 1 : 0;
261: return (nActiveSerializable - exclude > 0);
262: }
263:
264: /**
265: * Get the earliest LSN of all the active transactions, for checkpoint.
266: */
267: public long getFirstActiveLsn() throws DatabaseException {
268:
269: /*
270: * Note that the latching hierarchy calls for getting allTxnLatch
271: * first, then synchronizing on individual txns.
272: */
273: long firstActive = DbLsn.NULL_LSN;
274: allTxnLatch.acquire();
275: try {
276: Iterator iter = allTxns.iterator();
277: while (iter.hasNext()) {
278: long txnFirstActive = ((Txn) iter.next())
279: .getFirstActiveLsn();
280: if (firstActive == DbLsn.NULL_LSN) {
281: firstActive = txnFirstActive;
282: } else if (txnFirstActive != DbLsn.NULL_LSN) {
283: if (DbLsn.compareTo(txnFirstActive, firstActive) < 0) {
284: firstActive = txnFirstActive;
285: }
286: }
287: }
288: } finally {
289: allTxnLatch.release();
290: }
291: return firstActive;
292: }
293:
294: /*
295: * Statistics
296: */
297:
298: /**
299: * Collect transaction related stats.
300: */
301: public TransactionStats txnStat(StatsConfig config)
302: throws DatabaseException {
303:
304: TransactionStats stats = new TransactionStats();
305: allTxnLatch.acquire();
306: try {
307: stats.setNCommits(numCommits);
308: stats.setNAborts(numAborts);
309: stats.setNXAPrepares(numXAPrepares);
310: stats.setNXACommits(numXACommits);
311: stats.setNXAAborts(numXAAborts);
312: stats.setNActive(allTxns.size());
313: TransactionStats.Active[] activeSet = new TransactionStats.Active[stats
314: .getNActive()];
315: stats.setActiveTxns(activeSet);
316: Iterator iter = allTxns.iterator();
317: int i = 0;
318: while (iter.hasNext()) {
319: Locker txn = (Locker) iter.next();
320: activeSet[i] = new TransactionStats.Active(txn
321: .toString(), txn.getId(), 0);
322: i++;
323: }
324: if (config.getClear()) {
325: numCommits = 0;
326: numAborts = 0;
327: numXACommits = 0;
328: numXAAborts = 0;
329: }
330: } finally {
331: allTxnLatch.release();
332: }
333: return stats;
334: }
335:
336: /**
337: * Collect lock related stats.
338: */
339: public LockStats lockStat(StatsConfig config)
340: throws DatabaseException {
341:
342: return lockManager.lockStat(config);
343: }
344: }
|